summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt2
-rw-r--r--CMakeModules/FindSimpleIni.cmake19
-rw-r--r--CMakeModules/Findinih.cmake27
-rw-r--r--dist/languages/.tx/config5
-rw-r--r--externals/CMakeLists.txt10
-rw-r--r--externals/inih/CMakeLists.txt13
m---------externals/inih/inih0
m---------externals/simpleini0
-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.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt52
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt30
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt61
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt14
-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/AboutFragment.kt8
-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/SettingsSearchFragment.kt7
-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/SettingsViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt93
-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/GameIconUtils.kt7
-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.kt24
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt11
-rw-r--r--src/android/app/src/main/jni/android_config.cpp70
-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)2
-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/native.cpp15
-rw-r--r--src/android/app/src/main/jni/native_config.cpp23
-rw-r--r--src/android/app/src/main/res/drawable/ic_audio.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_code.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_graphics.xml9
-rw-r--r--src/android/app/src/main/res/drawable/ic_system_settings.xml9
-rw-r--r--src/android/app/src/main/res/layout-w600dp/fragment_about.xml233
-rw-r--r--src/android/app/src/main/res/layout/card_home_option.xml4
-rw-r--r--src/android/app/src/main/res/layout/fragment_about.xml10
-rw-r--r--src/android/app/src/main/res/layout/fragment_emulation.xml5
-rw-r--r--src/android/app/src/main/res/layout/fragment_home_settings.xml9
-rw-r--r--src/android/app/src/main/res/layout/fragment_search.xml1
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting.xml72
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml8
-rw-r--r--src/android/app/src/main/res/layout/list_item_settings_header.xml3
-rw-r--r--src/android/app/src/main/res/values/arrays.xml2
-rw-r--r--src/android/app/src/main/res/values/strings.xml7
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp2
-rw-r--r--src/audio_core/opus/decoder.cpp2
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp2
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp2
-rw-r--r--src/audio_core/sink/sink_stream.cpp12
-rw-r--r--src/audio_core/sink/sink_stream.h6
-rw-r--r--src/common/settings.cpp6
-rw-r--r--src/common/settings.h44
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/common/settings_input.cpp9
-rw-r--r--src/common/settings_input.h7
-rw-r--r--src/core/CMakeLists.txt20
-rw-r--r--src/core/arm/arm_interface.cpp16
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/core_timing.h2
-rw-r--r--src/core/hid/emulated_console.h8
-rw-r--r--src/core/hid/emulated_controller.cpp95
-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.h95
-rw-r--r--src/core/hid/input_interpreter.cpp11
-rw-r--r--src/core/hid/input_interpreter.h4
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp4
-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.cpp3
-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/controller_base.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-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.cpp582
-rw-r--r--src/core/hle/service/hid/controllers/npad.h205
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp88
-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.cpp11
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h6
-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.cpp2859
-rw-r--r--src/core/hle/service/hid/hid.h212
-rw-r--r--src/core/hle/service/hid/hid_debug_server.cpp159
-rw-r--r--src/core/hle/service/hid/hid_debug_server.h26
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.cpp99
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.h54
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2371
-rw-r--r--src/core/hle/service/hid/hid_server.h149
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp539
-rw-r--r--src/core/hle/service/hid/hid_system_server.h63
-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/irs.h5
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp241
-rw-r--r--src/core/hle/service/hid/resource_manager.h111
-rw-r--r--src/core/hle/service/ldn/ldn.cpp10
-rw-r--r--src/core/hle/service/nfc/common/device_manager.cpp3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp27
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h9
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.cpp12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp19
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_slot.h2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp20
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h2
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h2
-rw-r--r--src/core/hle/service/nvnflinger/status.h2
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp34
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.h25
-rw-r--r--src/core/hle/service/set/set_sys.cpp96
-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/hle/service/vi/display/vi_display.cpp2
-rw-r--r--src/core/memory.cpp25
-rw-r--r--src/core/memory/cheat_engine.cpp12
-rw-r--r--src/frontend_common/CMakeLists.txt10
-rw-r--r--src/frontend_common/config.cpp1008
-rw-r--r--src/frontend_common/config.h211
-rw-r--r--src/input_common/drivers/gc_adapter.cpp8
-rw-r--r--src/input_common/drivers/joycon.cpp8
-rw-r--r--src/input_common/drivers/sdl_driver.cpp20
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/udp_client.cpp8
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp6
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp6
-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/emit_spirv_image.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h2
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h2
-rw-r--r--src/shader_recompiler/frontend/ir/modifiers.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp29
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp1
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp10
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp79
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h5
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/fence_manager.h5
-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_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp9
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h11
-rw-r--r--src/yuzu/CMakeLists.txt57
-rw-r--r--src/yuzu/applets/qt_controller.cpp1
-rw-r--r--src/yuzu/configuration/config.cpp1306
-rw-r--r--src/yuzu/configuration/config.h179
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_camera.cpp2
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-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.cpp47
-rw-r--r--src/yuzu/configuration/configure_input_player.ui389
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp45
-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_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.cpp517
-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.cpp13
-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.cpp627
-rw-r--r--src/yuzu/main.h31
-rw-r--r--src/yuzu/main.ui22
-rw-r--r--src/yuzu/uisettings.cpp65
-rw-r--r--src/yuzu/uisettings.h89
-rw-r--r--src/yuzu/util/util.cpp15
-rw-r--r--src/yuzu/util/util.h3
-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.cpp5
250 files changed, 10766 insertions, 8973 deletions
diff --git a/.gitmodules b/.gitmodules
index b72a2ec8c..45dd0d259 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,9 +4,6 @@
4[submodule "enet"] 4[submodule "enet"]
5 path = externals/enet 5 path = externals/enet
6 url = https://github.com/lsalzman/enet.git 6 url = https://github.com/lsalzman/enet.git
7[submodule "inih"]
8 path = externals/inih/inih
9 url = https://github.com/benhoyt/inih.git
10[submodule "cubeb"] 7[submodule "cubeb"]
11 path = externals/cubeb 8 path = externals/cubeb
12 url = https://github.com/mozilla/cubeb.git 9 url = https://github.com/mozilla/cubeb.git
@@ -61,3 +58,6 @@
61[submodule "breakpad"] 58[submodule "breakpad"]
62 path = externals/breakpad 59 path = externals/breakpad
63 url = https://github.com/yuzu-emu/breakpad.git 60 url = https://github.com/yuzu-emu/breakpad.git
61[submodule "simpleini"]
62 path = externals/simpleini
63 url = https://github.com/brofield/simpleini.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9c35e0946..ec7975b87 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -285,12 +285,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
285find_package(Boost 1.79.0 REQUIRED context) 285find_package(Boost 1.79.0 REQUIRED context)
286find_package(enet 1.3 MODULE) 286find_package(enet 1.3 MODULE)
287find_package(fmt 9 REQUIRED) 287find_package(fmt 9 REQUIRED)
288find_package(inih 52 MODULE COMPONENTS INIReader)
289find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) 288find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
290find_package(lz4 REQUIRED) 289find_package(lz4 REQUIRED)
291find_package(nlohmann_json 3.8 REQUIRED) 290find_package(nlohmann_json 3.8 REQUIRED)
292find_package(Opus 1.3 MODULE) 291find_package(Opus 1.3 MODULE)
293find_package(RenderDoc MODULE) 292find_package(RenderDoc MODULE)
293find_package(SimpleIni MODULE)
294find_package(stb MODULE) 294find_package(stb MODULE)
295find_package(VulkanMemoryAllocator CONFIG) 295find_package(VulkanMemoryAllocator CONFIG)
296find_package(ZLIB 1.2 REQUIRED) 296find_package(ZLIB 1.2 REQUIRED)
diff --git a/CMakeModules/FindSimpleIni.cmake b/CMakeModules/FindSimpleIni.cmake
new file mode 100644
index 000000000..ce75d7690
--- /dev/null
+++ b/CMakeModules/FindSimpleIni.cmake
@@ -0,0 +1,19 @@
1# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4
5find_path(SimpleIni_INCLUDE_DIR SimpleIni.h)
6
7include(FindPackageHandleStandardArgs)
8find_package_handle_standard_args(SimpleIni
9 REQUIRED_VARS SimpleIni_INCLUDE_DIR
10)
11
12if (SimpleIni_FOUND AND NOT TARGET SimpleIni::SimpleIni)
13 add_library(SimpleIni::SimpleIni INTERFACE IMPORTED)
14 set_target_properties(SimpleIni::SimpleIni PROPERTIES
15 INTERFACE_INCLUDE_DIRECTORIES "${SimpleIni_INCLUDE_DIR}"
16 )
17endif()
18
19mark_as_advanced(SimpleIni_INCLUDE_DIR)
diff --git a/CMakeModules/Findinih.cmake b/CMakeModules/Findinih.cmake
deleted file mode 100644
index 791befebd..000000000
--- a/CMakeModules/Findinih.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
1# SPDX-FileCopyrightText: 2022 Alexandre Bouvier <contact@amb.tf>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4
5find_package(PkgConfig QUIET)
6pkg_search_module(INIH QUIET IMPORTED_TARGET inih)
7if (INIReader IN_LIST inih_FIND_COMPONENTS)
8 pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader)
9 if (INIREADER_FOUND)
10 set(inih_INIReader_FOUND TRUE)
11 endif()
12endif()
13
14include(FindPackageHandleStandardArgs)
15find_package_handle_standard_args(inih
16 REQUIRED_VARS INIH_LINK_LIBRARIES
17 VERSION_VAR INIH_VERSION
18 HANDLE_COMPONENTS
19)
20
21if (inih_FOUND AND NOT TARGET inih::inih)
22 add_library(inih::inih ALIAS PkgConfig::INIH)
23endif()
24
25if (inih_FOUND AND inih_INIReader_FOUND AND NOT TARGET inih::INIReader)
26 add_library(inih::INIReader ALIAS PkgConfig::INIREADER)
27endif()
diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config
index 30e76b925..cca7b3d67 100644
--- a/dist/languages/.tx/config
+++ b/dist/languages/.tx/config
@@ -6,3 +6,8 @@ file_filter = <lang>.ts
6source_file = en.ts 6source_file = en.ts
7source_lang = en 7source_lang = en
8type = QT 8type = QT
9
10[o:yuzu-emulator:p:yuzu:r:yuzu-android]
11file_filter = ../../src/android/app/src/main/res/values-<lang>/strings.xml
12source_file = ../../src/android/app/src/main/res/values/strings.xml
13type = ANDROID
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index be8b0b5e8..fc922c31b 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -34,11 +34,6 @@ endif()
34# Glad 34# Glad
35add_subdirectory(glad) 35add_subdirectory(glad)
36 36
37# inih
38if (NOT TARGET inih::INIReader)
39 add_subdirectory(inih)
40endif()
41
42# mbedtls 37# mbedtls
43add_subdirectory(mbedtls) 38add_subdirectory(mbedtls)
44target_include_directories(mbedtls PUBLIC ./mbedtls/include) 39target_include_directories(mbedtls PUBLIC ./mbedtls/include)
@@ -295,3 +290,8 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
295 target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB) 290 target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
296 endif() 291 endif()
297endif() 292endif()
293
294# SimpleIni
295if (NOT TARGET SimpleIni::SimpleIni)
296 add_subdirectory(simpleini)
297endif()
diff --git a/externals/inih/CMakeLists.txt b/externals/inih/CMakeLists.txt
deleted file mode 100644
index ebb60a976..000000000
--- a/externals/inih/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
1# SPDX-FileCopyrightText: 2014 Gui Andrade <admin@archshift.com>
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4add_library(inih
5 inih/ini.c
6 inih/ini.h
7 inih/cpp/INIReader.cpp
8 inih/cpp/INIReader.h
9)
10
11create_target_directory_groups(inih)
12target_include_directories(inih INTERFACE inih/cpp)
13add_library(inih::INIReader ALIAS inih)
diff --git a/externals/inih/inih b/externals/inih/inih
deleted file mode 160000
Subproject 9cecf0643da0846e77f64d10a126d9f48b9e05e
diff --git a/externals/simpleini b/externals/simpleini
new file mode 160000
Subproject 382ddbb4b92c0b26aa1b32cefba2002119a5b1f
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..f2ba2504c 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)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index 054e4b755..f41d7bdbf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -373,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
373 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder() 373 val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
374 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder() 374 .getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
375 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 375 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
376 val isEmulationActive = emulationViewModel.emulationStarted.value &&
377 !emulationViewModel.isEmulationStopping.value
376 pictureInPictureParamsBuilder.setAutoEnterEnabled( 378 pictureInPictureParamsBuilder.setAutoEnterEnabled(
377 BooleanSetting.PICTURE_IN_PICTURE.boolean 379 BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
378 ) 380 )
379 } 381 }
380 setPictureInPictureParams(pictureInPictureParamsBuilder.build()) 382 setPictureInPictureParams(pictureInPictureParamsBuilder.build())
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
index 0c82cdba8..2ef638559 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
@@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
22import androidx.core.graphics.drawable.toDrawable 22import androidx.core.graphics.drawable.toDrawable
23import androidx.documentfile.provider.DocumentFile 23import androidx.documentfile.provider.DocumentFile
24import androidx.lifecycle.ViewModelProvider 24import androidx.lifecycle.ViewModelProvider
25import androidx.lifecycle.lifecycleScope
25import androidx.navigation.findNavController 26import androidx.navigation.findNavController
26import androidx.preference.PreferenceManager 27import androidx.preference.PreferenceManager
27import androidx.recyclerview.widget.AsyncDifferConfig 28import androidx.recyclerview.widget.AsyncDifferConfig
28import androidx.recyclerview.widget.DiffUtil 29import androidx.recyclerview.widget.DiffUtil
29import androidx.recyclerview.widget.ListAdapter 30import androidx.recyclerview.widget.ListAdapter
30import androidx.recyclerview.widget.RecyclerView 31import androidx.recyclerview.widget.RecyclerView
32import kotlinx.coroutines.Dispatchers
33import kotlinx.coroutines.launch
34import kotlinx.coroutines.withContext
31import org.yuzu.yuzu_emu.HomeNavigationDirections 35import org.yuzu.yuzu_emu.HomeNavigationDirections
32import org.yuzu.yuzu_emu.R 36import org.yuzu.yuzu_emu.R
33import org.yuzu.yuzu_emu.YuzuApplication 37import org.yuzu.yuzu_emu.YuzuApplication
@@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
92 data = Uri.parse(holder.game.path) 96 data = Uri.parse(holder.game.path)
93 } 97 }
94 98
95 val layerDrawable = ResourcesCompat.getDrawable( 99 activity.lifecycleScope.launch {
96 YuzuApplication.appContext.resources, 100 withContext(Dispatchers.IO) {
97 R.drawable.shortcut, 101 val layerDrawable = ResourcesCompat.getDrawable(
98 null 102 YuzuApplication.appContext.resources,
99 ) as LayerDrawable 103 R.drawable.shortcut,
100 layerDrawable.setDrawableByLayerId( 104 null
101 R.id.shortcut_foreground, 105 ) as LayerDrawable
102 GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources) 106 layerDrawable.setDrawableByLayerId(
103 ) 107 R.id.shortcut_foreground,
104 val inset = YuzuApplication.appContext.resources 108 GameIconUtils.getGameIcon(activity, holder.game)
105 .getDimensionPixelSize(R.dimen.icon_inset) 109 .toDrawable(YuzuApplication.appContext.resources)
106 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
107 val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
108 .setShortLabel(holder.game.title)
109 .setIcon(
110 IconCompat.createWithAdaptiveBitmap(
111 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
112 ) 110 )
113 ) 111 val inset = YuzuApplication.appContext.resources
114 .setIntent(openIntent) 112 .getDimensionPixelSize(R.dimen.icon_inset)
115 .build() 113 layerDrawable.setLayerInset(1, inset, inset, inset, inset)
116 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut) 114 val shortcut =
115 ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
116 .setShortLabel(holder.game.title)
117 .setIcon(
118 IconCompat.createWithAdaptiveBitmap(
119 layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
120 )
121 )
122 .setIntent(openIntent)
123 .build()
124 ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
125 }
126 }
117 127
118 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game) 128 val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
119 view.findNavController().navigate(action) 129 view.findNavController().navigate(action)
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 08e2a973d..d005c656e 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
@@ -7,7 +7,7 @@ import android.text.TextUtils
7import android.widget.Toast 7import android.widget.Toast
8import org.yuzu.yuzu_emu.R 8import org.yuzu.yuzu_emu.R
9import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 10import org.yuzu.yuzu_emu.utils.NativeConfig
11 11
12object Settings { 12object Settings {
13 private val context get() = YuzuApplication.appContext 13 private val context get() = YuzuApplication.appContext
@@ -19,7 +19,7 @@ object Settings {
19 context.getString(R.string.ini_saved), 19 context.getString(R.string.ini_saved),
20 Toast.LENGTH_SHORT 20 Toast.LENGTH_SHORT
21 ).show() 21 ).show()
22 SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG) 22 NativeConfig.saveSettings()
23 } else { 23 } else {
24 // TODO: Save custom game settings 24 // TODO: Save custom game settings
25 Toast.makeText( 25 Toast.makeText(
@@ -82,7 +82,6 @@ object Settings {
82 82
83 enum class MenuTag(val titleId: Int) { 83 enum class MenuTag(val titleId: Int) {
84 SECTION_ROOT(R.string.advanced_settings), 84 SECTION_ROOT(R.string.advanced_settings),
85 SECTION_GENERAL(R.string.preferences_general),
86 SECTION_SYSTEM(R.string.preferences_system), 85 SECTION_SYSTEM(R.string.preferences_system),
87 SECTION_RENDERER(R.string.preferences_graphics), 86 SECTION_RENDERER(R.string.preferences_graphics),
88 SECTION_AUDIO(R.string.preferences_audio), 87 SECTION_AUDIO(R.string.preferences_audio),
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
index 522cc49df..425160024 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/RunnableSetting.kt
@@ -3,10 +3,13 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.DrawableRes
7
6class RunnableSetting( 8class RunnableSetting(
7 titleId: Int, 9 titleId: Int,
8 descriptionId: Int, 10 descriptionId: Int,
9 val isRuntimeRunnable: Boolean, 11 val isRuntimeRunnable: Boolean,
12 @DrawableRes val iconId: Int = 0,
10 val runnable: () -> Unit 13 val runnable: () -> Unit
11) : SettingsItem(emptySetting, titleId, descriptionId) { 14) : SettingsItem(emptySetting, titleId, descriptionId) {
12 override val type = TYPE_RUNNABLE 15 override val type = TYPE_RUNNABLE
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..6aba69dbe 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,7 +73,7 @@ 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 )
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
index b343e527e..94953b18a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SubmenuSetting.kt
@@ -3,11 +3,14 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.model.view 4package org.yuzu.yuzu_emu.features.settings.model.view
5 5
6import androidx.annotation.DrawableRes
7import androidx.annotation.StringRes
6import org.yuzu.yuzu_emu.features.settings.model.Settings 8import org.yuzu.yuzu_emu.features.settings.model.Settings
7 9
8class SubmenuSetting( 10class SubmenuSetting(
9 titleId: Int, 11 @StringRes titleId: Int,
10 descriptionId: Int, 12 @StringRes descriptionId: Int,
13 @DrawableRes val iconId: Int,
11 val menuKey: Settings.MenuTag 14 val menuKey: Settings.MenuTag
12) : SettingsItem(emptySetting, titleId, descriptionId) { 15) : SettingsItem(emptySetting, titleId, descriptionId) {
13 override val type = TYPE_SUBMENU 16 override val type = TYPE_SUBMENU
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..48bdbdd75 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
@@ -21,7 +21,6 @@ import androidx.navigation.navArgs
21import com.google.android.material.color.MaterialColors 21import com.google.android.material.color.MaterialColors
22import kotlinx.coroutines.flow.collectLatest 22import kotlinx.coroutines.flow.collectLatest
23import kotlinx.coroutines.launch 23import kotlinx.coroutines.launch
24import org.yuzu.yuzu_emu.NativeLibrary
25import java.io.IOException 24import java.io.IOException
26import org.yuzu.yuzu_emu.R 25import org.yuzu.yuzu_emu.R
27import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding 26import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
@@ -165,11 +164,12 @@ class SettingsActivity : AppCompatActivity() {
165 settingsViewModel.shouldSave = false 164 settingsViewModel.shouldSave = false
166 165
167 // Delete settings file because the user may have changed values that do not exist in the UI 166 // Delete settings file because the user may have changed values that do not exist in the UI
167 NativeConfig.unloadConfig()
168 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG) 168 val settingsFile = SettingsFile.getSettingsFile(SettingsFile.FILE_NAME_CONFIG)
169 if (!settingsFile.delete()) { 169 if (!settingsFile.delete()) {
170 throw IOException("Failed to delete $settingsFile") 170 throw IOException("Failed to delete $settingsFile")
171 } 171 }
172 NativeLibrary.reloadSettings() 172 NativeConfig.initializeConfig()
173 173
174 Toast.makeText( 174 Toast.makeText(
175 applicationContext, 175 applicationContext,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 70d8ec14b..769baf744 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
20import androidx.navigation.findNavController 20import androidx.navigation.findNavController
21import androidx.navigation.fragment.navArgs 21import androidx.navigation.fragment.navArgs
22import androidx.recyclerview.widget.LinearLayoutManager 22import androidx.recyclerview.widget.LinearLayoutManager
23import com.google.android.material.divider.MaterialDividerItemDecoration
24import com.google.android.material.transition.MaterialSharedAxis 23import com.google.android.material.transition.MaterialSharedAxis
25import kotlinx.coroutines.flow.collectLatest 24import kotlinx.coroutines.flow.collectLatest
26import kotlinx.coroutines.launch 25import kotlinx.coroutines.launch
@@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
68 ) 67 )
69 68
70 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId) 69 binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
71 val dividerDecoration = MaterialDividerItemDecoration(
72 requireContext(),
73 LinearLayoutManager.VERTICAL
74 )
75 dividerDecoration.isLastItemDecorated = false
76 binding.listSettings.apply { 70 binding.listSettings.apply {
77 adapter = settingsAdapter 71 adapter = settingsAdapter
78 layoutManager = LinearLayoutManager(requireContext()) 72 layoutManager = LinearLayoutManager(requireContext())
79 addItemDecoration(dividerDecoration)
80 } 73 }
81 74
82 binding.toolbarSettings.setNavigationOnClickListener { 75 binding.toolbarSettings.setNavigationOnClickListener {
@@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
94 } 87 }
95 } 88 }
96 } 89 }
97 launch {
98 settingsViewModel.isUsingSearch.collectLatest {
99 if (it) {
100 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
101 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
102 } else {
103 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
104 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
105 }
106 }
107 }
108 } 90 }
109 91
110 if (args.menuTag == Settings.MenuTag.SECTION_ROOT) { 92 if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
@@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
112 binding.toolbarSettings.setOnMenuItemClickListener { 94 binding.toolbarSettings.setOnMenuItemClickListener {
113 when (it.itemId) { 95 when (it.itemId) {
114 R.id.action_search -> { 96 R.id.action_search -> {
115 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
116 exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
117 view.findNavController() 97 view.findNavController()
118 .navigate(R.id.action_settingsFragment_to_settingsSearchFragment) 98 .navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
119 true 99 true
@@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
129 setInsets() 109 setInsets()
130 } 110 }
131 111
132 override fun onResume() {
133 super.onResume()
134 settingsViewModel.setIsUsingSearch(false)
135 }
136
137 private fun setInsets() { 112 private fun setInsets() {
138 ViewCompat.setOnApplyWindowInsetsListener( 113 ViewCompat.setOnApplyWindowInsetsListener(
139 binding.root 114 binding.root
@@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
144 val leftInsets = barInsets.left + cutoutInsets.left 119 val leftInsets = barInsets.left + cutoutInsets.left
145 val rightInsets = barInsets.right + cutoutInsets.right 120 val rightInsets = barInsets.right + cutoutInsets.right
146 121
147 val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
148 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams 122 val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
149 mlpSettingsList.leftMargin = sideMargin + leftInsets 123 mlpSettingsList.leftMargin = leftInsets
150 mlpSettingsList.rightMargin = sideMargin + rightInsets 124 mlpSettingsList.rightMargin = rightInsets
151 binding.listSettings.layoutParams = mlpSettingsList 125 binding.listSettings.layoutParams = mlpSettingsList
152 binding.listSettings.updatePadding( 126 binding.listSettings.updatePadding(
153 bottom = barInsets.bottom 127 bottom = barInsets.bottom
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 766414a6c..8b71e32f3 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
@@ -3,7 +3,6 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.Context
7import android.content.SharedPreferences 6import android.content.SharedPreferences
8import android.os.Build 7import android.os.Build
9import android.widget.Toast 8import android.widget.Toast
@@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
32 private val preferences: SharedPreferences 31 private val preferences: SharedPreferences
33 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 32 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
34 33
35 private val context: Context get() = YuzuApplication.appContext
36
37 // Extension for populating settings list based on paired settings 34 // Extension for populating settings list based on paired settings
38 fun ArrayList<SettingsItem>.add(key: String) { 35 fun ArrayList<SettingsItem>.add(key: String) {
39 val item = SettingsItem.settingsItems[key]!! 36 val item = SettingsItem.settingsItems[key]!!
@@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
53 val sl = ArrayList<SettingsItem>() 50 val sl = ArrayList<SettingsItem>()
54 when (menuTag) { 51 when (menuTag) {
55 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl) 52 Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
56 Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
57 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl) 53 Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
58 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl) 54 Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
59 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl) 55 Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
@@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
75 71
76 private fun addConfigSettings(sl: ArrayList<SettingsItem>) { 72 private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
77 sl.apply { 73 sl.apply {
78 add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
79 add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
80 add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
81 add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
82 add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
83 add( 74 add(
84 RunnableSetting(R.string.reset_to_default, 0, false) { 75 SubmenuSetting(
85 settingsViewModel.setShouldShowResetSettingsDialog(true) 76 R.string.preferences_system,
86 } 77 R.string.preferences_system_description,
78 R.drawable.ic_system_settings,
79 Settings.MenuTag.SECTION_SYSTEM
80 )
81 )
82 add(
83 SubmenuSetting(
84 R.string.preferences_graphics,
85 R.string.preferences_graphics_description,
86 R.drawable.ic_graphics,
87 Settings.MenuTag.SECTION_RENDERER
88 )
89 )
90 add(
91 SubmenuSetting(
92 R.string.preferences_audio,
93 R.string.preferences_audio_description,
94 R.drawable.ic_audio,
95 Settings.MenuTag.SECTION_AUDIO
96 )
97 )
98 add(
99 SubmenuSetting(
100 R.string.preferences_debug,
101 R.string.preferences_debug_description,
102 R.drawable.ic_code,
103 Settings.MenuTag.SECTION_DEBUG
104 )
105 )
106 add(
107 RunnableSetting(
108 R.string.reset_to_default,
109 R.string.reset_to_default_description,
110 false,
111 R.drawable.ic_restore
112 ) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
87 ) 113 )
88 } 114 }
89 } 115 }
90 116
91 private fun addGeneralSettings(sl: ArrayList<SettingsItem>) { 117 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
92 sl.apply { 118 sl.apply {
93 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key) 119 add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
94 add(ShortSetting.RENDERER_SPEED_LIMIT.key) 120 add(ShortSetting.RENDERER_SPEED_LIMIT.key)
95 add(IntSetting.CPU_ACCURACY.key)
96 add(BooleanSetting.PICTURE_IN_PICTURE.key)
97 }
98 }
99
100 private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
101 sl.apply {
102 add(BooleanSetting.USE_DOCKED_MODE.key) 121 add(BooleanSetting.USE_DOCKED_MODE.key)
103 add(IntSetting.REGION_INDEX.key) 122 add(IntSetting.REGION_INDEX.key)
104 add(IntSetting.LANGUAGE_INDEX.key) 123 add(IntSetting.LANGUAGE_INDEX.key)
@@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
116 add(IntSetting.RENDERER_ANTI_ALIASING.key) 135 add(IntSetting.RENDERER_ANTI_ALIASING.key)
117 add(IntSetting.RENDERER_SCREEN_LAYOUT.key) 136 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
118 add(IntSetting.RENDERER_ASPECT_RATIO.key) 137 add(IntSetting.RENDERER_ASPECT_RATIO.key)
138 add(BooleanSetting.PICTURE_IN_PICTURE.key)
119 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key) 139 add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
120 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) 140 add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
121 add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) 141 add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
@@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
249 add(BooleanSetting.RENDERER_DEBUG.key) 269 add(BooleanSetting.RENDERER_DEBUG.key)
250 270
251 add(HeaderSetting(R.string.cpu)) 271 add(HeaderSetting(R.string.cpu))
272 add(IntSetting.CPU_ACCURACY.key)
252 add(BooleanSetting.CPU_DEBUG_MODE.key) 273 add(BooleanSetting.CPU_DEBUG_MODE.key)
253 add(SettingsItem.FASTMEM_COMBINED) 274 add(SettingsItem.FASTMEM_COMBINED)
254 } 275 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
index 83a2e94f1..036195624 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/RunnableViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.core.content.res.ResourcesCompat
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 9import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
9import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
@@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
16 17
17 override fun bind(item: SettingsItem) { 18 override fun bind(item: SettingsItem) {
18 setting = item as RunnableSetting 19 setting = item as RunnableSetting
20 if (item.iconId != 0) {
21 binding.icon.visibility = View.VISIBLE
22 binding.icon.setImageDrawable(
23 ResourcesCompat.getDrawable(
24 binding.icon.resources,
25 item.iconId,
26 binding.icon.context.theme
27 )
28 )
29 } else {
30 binding.icon.visibility = View.GONE
31 }
32
19 binding.textSettingName.setText(item.nameId) 33 binding.textSettingName.setText(item.nameId)
20 if (item.descriptionId != 0) { 34 if (item.descriptionId != 0) {
21 binding.textSettingDescription.setText(item.descriptionId) 35 binding.textSettingDescription.setText(item.descriptionId)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
index 1cf581a9d..8100c65dd 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SubmenuViewHolder.kt
@@ -4,6 +4,7 @@
4package org.yuzu.yuzu_emu.features.settings.ui.viewholder 4package org.yuzu.yuzu_emu.features.settings.ui.viewholder
5 5
6import android.view.View 6import android.view.View
7import androidx.core.content.res.ResourcesCompat
7import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding 8import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem 9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
9import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting 10import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
@@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
15 16
16 override fun bind(item: SettingsItem) { 17 override fun bind(item: SettingsItem) {
17 this.item = item as SubmenuSetting 18 this.item = item as SubmenuSetting
19 if (item.iconId != 0) {
20 binding.icon.visibility = View.VISIBLE
21 binding.icon.setImageDrawable(
22 ResourcesCompat.getDrawable(
23 binding.icon.resources,
24 item.iconId,
25 binding.icon.context.theme
26 )
27 )
28 } else {
29 binding.icon.visibility = View.GONE
30 }
31
18 binding.textSettingName.setText(item.nameId) 32 binding.textSettingName.setText(item.nameId)
19 if (item.descriptionId != 0) { 33 if (item.descriptionId != 0) {
20 binding.textSettingDescription.setText(item.descriptionId) 34 binding.textSettingDescription.setText(item.descriptionId)
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/AboutFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
index 2ff827c6b..a1620fbb7 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AboutFragment.kt
@@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
114 val leftInsets = barInsets.left + cutoutInsets.left 114 val leftInsets = barInsets.left + cutoutInsets.left
115 val rightInsets = barInsets.right + cutoutInsets.right 115 val rightInsets = barInsets.right + cutoutInsets.right
116 116
117 val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams 117 val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
118 mlpAppBar.leftMargin = leftInsets 118 mlpToolbar.leftMargin = leftInsets
119 mlpAppBar.rightMargin = rightInsets 119 mlpToolbar.rightMargin = rightInsets
120 binding.appbarAbout.layoutParams = mlpAppBar 120 binding.toolbarAbout.layoutParams = mlpToolbar
121 121
122 val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams 122 val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
123 mlpScrollAbout.leftMargin = leftInsets 123 mlpScrollAbout.leftMargin = leftInsets
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/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
index 9d0594c6e..f95d545bf 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SettingsSearchFragment.kt
@@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
40 40
41 override fun onCreate(savedInstanceState: Bundle?) { 41 override fun onCreate(savedInstanceState: Bundle?) {
42 super.onCreate(savedInstanceState) 42 super.onCreate(savedInstanceState)
43 enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) 43 enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
44 returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) 44 returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
45 reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
46 exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
45 } 47 }
46 48
47 override fun onCreateView( 49 override fun onCreateView(
@@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
55 57
56 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 58 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
57 super.onViewCreated(view, savedInstanceState) 59 super.onViewCreated(view, savedInstanceState)
58 settingsViewModel.setIsUsingSearch(true)
59 60
60 if (savedInstanceState != null) { 61 if (savedInstanceState != null) {
61 binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT)) 62 binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
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/SettingsViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/SettingsViewModel.kt
index 53fa7a8de..6f947674e 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
@@ -29,9 +29,6 @@ class SettingsViewModel : ViewModel() {
29 val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList 29 val shouldReloadSettingsList: StateFlow<Boolean> get() = _shouldReloadSettingsList
30 private val _shouldReloadSettingsList = MutableStateFlow(false) 30 private val _shouldReloadSettingsList = MutableStateFlow(false)
31 31
32 val isUsingSearch: StateFlow<Boolean> get() = _isUsingSearch
33 private val _isUsingSearch = MutableStateFlow(false)
34
35 val sliderProgress: StateFlow<Int> get() = _sliderProgress 32 val sliderProgress: StateFlow<Int> get() = _sliderProgress
36 private val _sliderProgress = MutableStateFlow(-1) 33 private val _sliderProgress = MutableStateFlow(-1)
37 34
@@ -57,10 +54,6 @@ class SettingsViewModel : ViewModel() {
57 _shouldReloadSettingsList.value = value 54 _shouldReloadSettingsList.value = value
58 } 55 }
59 56
60 fun setIsUsingSearch(value: Boolean) {
61 _isUsingSearch.value = value
62 }
63
64 fun setSliderTextValue(value: Float, units: String) { 57 fun setSliderTextValue(value: Float, units: String) {
65 _sliderProgress.value = value.toInt() 58 _sliderProgress.value = value.toInt()
66 _sliderTextValue.value = String.format( 59 _sliderTextValue.value = String.format(
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..bd2f4cd25 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,7 +39,6 @@ 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
46import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment 43import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
47import org.yuzu.yuzu_emu.fragments.MessageDialogFragment 44import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
@@ -53,9 +50,6 @@ import org.yuzu.yuzu_emu.model.TaskViewModel
53import org.yuzu.yuzu_emu.utils.* 50import org.yuzu.yuzu_emu.utils.*
54import java.io.BufferedInputStream 51import java.io.BufferedInputStream
55import java.io.BufferedOutputStream 52import java.io.BufferedOutputStream
56import java.io.FileOutputStream
57import java.time.LocalDateTime
58import java.time.format.DateTimeFormatter
59import java.util.zip.ZipEntry 53import java.util.zip.ZipEntry
60import java.util.zip.ZipInputStream 54import java.util.zip.ZipInputStream
61 55
@@ -73,7 +67,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
73 67
74 // Get first subfolder in saves folder (should be the user folder) 68 // Get first subfolder in saves folder (should be the user folder)
75 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: "" 69 val savesFolderRoot get() = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
76 private var lastZipCreated: File? = null
77 70
78 override fun onCreate(savedInstanceState: Bundle?) { 71 override fun onCreate(savedInstanceState: Bundle?) {
79 val splashScreen = installSplashScreen() 72 val splashScreen = installSplashScreen()
@@ -632,6 +625,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
632 } 625 }
633 626
634 // Clear existing user data 627 // Clear existing user data
628 NativeConfig.unloadConfig()
635 File(DirectoryInitialization.userDirectory!!).deleteRecursively() 629 File(DirectoryInitialization.userDirectory!!).deleteRecursively()
636 630
637 // Copy archive to internal storage 631 // Copy archive to internal storage
@@ -650,6 +644,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
650 644
651 // Reinitialize relevant data 645 // Reinitialize relevant data
652 NativeLibrary.initializeSystem(true) 646 NativeLibrary.initializeSystem(true)
647 NativeConfig.initializeConfig()
653 gamesViewModel.reloadGames(false) 648 gamesViewModel.reloadGames(false)
654 649
655 return@newInstance getString(R.string.user_data_import_success) 650 return@newInstance getString(R.string.user_data_import_success)
@@ -657,74 +652,30 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
657 } 652 }
658 653
659 /** 654 /**
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. 655 * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
692 */ 656 */
693 fun exportSave() { 657 val exportSaves = registerForActivityResult(
694 CoroutineScope(Dispatchers.IO).launch { 658 ActivityResultContracts.CreateDocument("application/zip")
695 val wasZipCreated = zipSave() 659 ) { result ->
696 val lastZipFile = lastZipCreated 660 if (result == null) {
697 if (!wasZipCreated || lastZipFile == null) { 661 return@registerForActivityResult
698 withContext(Dispatchers.Main) { 662 }
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 663
708 withContext(Dispatchers.Main) { 664 IndeterminateProgressDialogFragment.newInstance(
709 val file = DocumentFile.fromSingleUri( 665 this,
710 this@MainActivity, 666 R.string.save_files_exporting,
711 DocumentsContract.buildDocumentUri( 667 false
712 DocumentProvider.AUTHORITY, 668 ) {
713 "${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}" 669 val zipResult = FileUtil.zipFromInternalStorage(
714 ) 670 File(savesFolderRoot),
715 )!! 671 savesFolderRoot,
716 val intent = Intent(Intent.ACTION_SEND) 672 BufferedOutputStream(contentResolver.openOutputStream(result))
717 .setDataAndType(file.uri, "application/zip") 673 )
718 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 674 return@newInstance when (zipResult) {
719 .putExtra(Intent.EXTRA_STREAM, file.uri) 675 TaskState.Completed -> getString(R.string.export_success)
720 startForResultExportSave.launch( 676 TaskState.Cancelled, TaskState.Failed -> getString(R.string.export_failed)
721 Intent.createChooser(
722 intent,
723 getString(R.string.share_save_file)
724 )
725 )
726 } 677 }
727 } 678 }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
728 } 679 }
729 680
730 private val startForResultExportSave = 681 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/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
index 654d62f52..2e9b0beb8 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
@@ -8,9 +8,9 @@ import android.graphics.BitmapFactory
8import android.widget.ImageView 8import android.widget.ImageView
9import androidx.core.graphics.drawable.toBitmap 9import androidx.core.graphics.drawable.toBitmap
10import androidx.core.graphics.drawable.toDrawable 10import androidx.core.graphics.drawable.toDrawable
11import androidx.lifecycle.LifecycleOwner
11import coil.ImageLoader 12import coil.ImageLoader
12import coil.decode.DataSource 13import coil.decode.DataSource
13import coil.executeBlocking
14import coil.fetch.DrawableResult 14import coil.fetch.DrawableResult
15import coil.fetch.FetchResult 15import coil.fetch.FetchResult
16import coil.fetch.Fetcher 16import coil.fetch.Fetcher
@@ -76,12 +76,13 @@ object GameIconUtils {
76 imageLoader.enqueue(request) 76 imageLoader.enqueue(request)
77 } 77 }
78 78
79 fun getGameIcon(game: Game): Bitmap { 79 suspend fun getGameIcon(lifecycleOwner: LifecycleOwner, game: Game): Bitmap {
80 val request = ImageRequest.Builder(YuzuApplication.appContext) 80 val request = ImageRequest.Builder(YuzuApplication.appContext)
81 .data(game) 81 .data(game)
82 .lifecycle(lifecycleOwner)
82 .error(R.drawable.default_icon) 83 .error(R.drawable.default_icon)
83 .build() 84 .build()
84 return imageLoader.executeBlocking(request) 85 return imageLoader.execute(request)
85 .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888) 86 .drawable!!.toBitmap(config = Bitmap.Config.ARGB_8888)
86 } 87 }
87} 88}
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..87e579fa7 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
@@ -4,6 +4,30 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6object NativeConfig { 6object NativeConfig {
7 /**
8 * Creates a Config object and opens the emulation config.
9 */
10 @Synchronized
11 external fun initializeConfig()
12
13 /**
14 * Destroys the stored config object. This automatically saves the existing config.
15 */
16 @Synchronized
17 external fun unloadConfig()
18
19 /**
20 * Reads values saved to the config file and saves them.
21 */
22 @Synchronized
23 external fun reloadSettings()
24
25 /**
26 * Saves settings values in memory to disk.
27 */
28 @Synchronized
29 external fun saveSettings()
30
7 external fun getBoolean(key: String, getDefault: Boolean): Boolean 31 external fun getBoolean(key: String, getDefault: Boolean): Boolean
8 external fun setBoolean(key: String, value: Boolean) 32 external fun setBoolean(key: String, value: Boolean)
9 33
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..3041c25c9
--- /dev/null
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -0,0 +1,70 @@
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 }
38}
39
40void AndroidConfig::ReadAndroidUIValues() {
41 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
42
43 ReadCategory(Settings::Category::Android);
44
45 EndGroup();
46}
47
48void AndroidConfig::SaveAndroidValues() {
49 if (global) {
50 SaveAndroidUIValues();
51 }
52
53 WriteToIni();
54}
55
56void AndroidConfig::SaveAndroidUIValues() {
57 BeginGroup(Settings::TranslateCategory(Settings::Category::Android));
58
59 WriteCategory(Settings::Category::Android);
60
61 EndGroup();
62}
63
64std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
65 auto& map = Settings::values.linkage.by_category;
66 if (map.contains(category)) {
67 return Settings::values.linkage.by_category[category];
68 }
69 return AndroidSettings::values.linkage.by_category[category];
70}
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..e679392fd
--- /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 494654af7..37bc33918 100644
--- a/src/android/app/src/main/jni/uisettings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -13,7 +13,7 @@ struct Values {
13 Settings::Linkage linkage; 13 Settings::Linkage linkage;
14 14
15 // Android 15 // Android
16 Settings::Setting<bool> picture_in_picture{linkage, true, "picture_in_picture", 16 Settings::Setting<bool> picture_in_picture{linkage, false, "picture_in_picture",
17 Settings::Category::Android}; 17 Settings::Category::Android};
18 Settings::Setting<s32> screen_layout{linkage, 18 Settings::Setting<s32> screen_layout{linkage,
19 5, 19 5,
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/native.cpp b/src/android/app/src/main/jni/native.cpp
index 64663b084..617288ae4 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"
@@ -664,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c
664 664
665void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, 665void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz,
666 jboolean reload) { 666 jboolean reload) {
667 // Create the default config.ini.
668 Config{};
669 // Initialize the emulated system. 667 // Initialize the emulated system.
670 if (!reload) { 668 if (!reload) {
671 EmulationSession::GetInstance().System().Initialize(); 669 EmulationSession::GetInstance().System().Initialize();
@@ -680,17 +678,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( 678void 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) {} 679 JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
682 680
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) { 681jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
695 jdoubleArray j_stats = env->NewDoubleArray(4); 682 jdoubleArray j_stats = env->NewDoubleArray(4);
696 683
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 8a704960c..8e81816e5 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -5,11 +5,14 @@
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
12#include "uisettings.h" 15std::unique_ptr<AndroidConfig> config;
13 16
14template <typename T> 17template <typename T>
15Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) { 18Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
@@ -28,6 +31,22 @@ Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
28 31
29extern "C" { 32extern "C" {
30 33
34void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) {
35 config = std::make_unique<AndroidConfig>();
36}
37
38void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) {
39 config.reset();
40}
41
42void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) {
43 config->AndroidConfig::ReloadAllValues();
44}
45
46void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) {
47 config->AndroidConfig::SaveAllValues();
48}
49
31jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, 50jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj,
32 jstring jkey, jboolean getDefault) { 51 jstring jkey, jboolean getDefault) {
33 auto setting = getSetting<bool>(env, jkey); 52 auto setting = getSetting<bool>(env, jkey);
diff --git a/src/android/app/src/main/res/drawable/ic_audio.xml b/src/android/app/src/main/res/drawable/ic_audio.xml
new file mode 100644
index 000000000..e306c3b0c
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_audio.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportHeight="24"
5 android:viewportWidth="24">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z" />
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_code.xml b/src/android/app/src/main/res/drawable/ic_code.xml
new file mode 100644
index 000000000..26f83b39b
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_code.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_graphics.xml b/src/android/app/src/main/res/drawable/ic_graphics.xml
new file mode 100644
index 000000000..2fdb5a4d6
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_graphics.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M160,840q-33,0 -56.5,-23.5T80,760v-560q0,-33 23.5,-56.5T160,120h560q33,0 56.5,23.5T800,200v80h80v80h-80v80h80v80h-80v80h80v80h-80v80q0,33 -23.5,56.5T720,840L160,840ZM160,760h560v-560L160,200v560ZM240,680h200v-160L240,520v160ZM480,400h160v-120L480,280v120ZM240,480h200v-200L240,280v200ZM480,680h160v-240L480,440v240ZM160,200v560,-560Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/drawable/ic_system_settings.xml b/src/android/app/src/main/res/drawable/ic_system_settings.xml
new file mode 100644
index 000000000..7701a2bab
--- /dev/null
+++ b/src/android/app/src/main/res/drawable/ic_system_settings.xml
@@ -0,0 +1,9 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 android:width="24dp"
3 android:height="24dp"
4 android:viewportWidth="960"
5 android:viewportHeight="960">
6 <path
7 android:fillColor="?attr/colorControlNormal"
8 android:pathData="M320,960q-17,0 -28.5,-11.5T280,920q0,-17 11.5,-28.5T320,880q17,0 28.5,11.5T360,920q0,17 -11.5,28.5T320,960ZM480,960q-17,0 -28.5,-11.5T440,920q0,-17 11.5,-28.5T480,880q17,0 28.5,11.5T520,920q0,17 -11.5,28.5T480,960ZM640,960q-17,0 -28.5,-11.5T600,920q0,-17 11.5,-28.5T640,880q17,0 28.5,11.5T680,920q0,17 -11.5,28.5T640,960ZM320,800q-33,0 -56.5,-23.5T240,720v-640q0,-33 23.5,-56.5T320,0h320q33,0 56.5,23.5T720,80v640q0,33 -23.5,56.5T640,800L320,800ZM320,720h320v-40L320,680v40ZM320,600h320v-400L320,200v400ZM320,120h320v-40L320,80v40ZM320,120v-40,40ZM320,720v-40,40Z"/>
9</vector>
diff --git a/src/android/app/src/main/res/layout-w600dp/fragment_about.xml b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
new file mode 100644
index 000000000..a26ffbc73
--- /dev/null
+++ b/src/android/app/src/main/res/layout-w600dp/fragment_about.xml
@@ -0,0 +1,233 @@
1<?xml version="1.0" encoding="utf-8"?>
2<androidx.coordinatorlayout.widget.CoordinatorLayout 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 android:id="@+id/coordinator_about"
6 android:layout_width="match_parent"
7 android:layout_height="match_parent"
8 android:background="?attr/colorSurface">
9
10 <com.google.android.material.appbar.AppBarLayout
11 android:id="@+id/appbar_about"
12 android:layout_width="match_parent"
13 android:layout_height="wrap_content"
14 android:fitsSystemWindows="true">
15
16 <com.google.android.material.appbar.MaterialToolbar
17 android:id="@+id/toolbar_about"
18 android:layout_width="match_parent"
19 android:layout_height="?attr/actionBarSize"
20 app:navigationIcon="@drawable/ic_back"
21 app:title="@string/about" />
22
23 </com.google.android.material.appbar.AppBarLayout>
24
25 <androidx.core.widget.NestedScrollView
26 android:id="@+id/scroll_about"
27 android:layout_width="match_parent"
28 android:layout_height="match_parent"
29 android:fadeScrollbars="false"
30 android:scrollbars="vertical"
31 app:layout_behavior="@string/appbar_scrolling_view_behavior">
32
33 <LinearLayout
34 android:id="@+id/content_about"
35 android:layout_width="match_parent"
36 android:layout_height="match_parent"
37 android:orientation="horizontal">
38
39 <ImageView
40 android:id="@+id/image_logo"
41 android:layout_width="200dp"
42 android:layout_height="200dp"
43 android:layout_gravity="center_horizontal"
44 android:padding="20dp"
45 android:src="@drawable/ic_yuzu_title" />
46
47 <LinearLayout
48 android:layout_width="wrap_content"
49 android:layout_height="wrap_content"
50 android:orientation="vertical">
51
52 <LinearLayout
53 android:layout_width="match_parent"
54 android:layout_height="wrap_content"
55 android:orientation="vertical"
56 android:paddingHorizontal="16dp"
57 android:paddingVertical="16dp">
58
59 <com.google.android.material.textview.MaterialTextView
60 style="@style/TextAppearance.Material3.TitleMedium"
61 android:layout_width="match_parent"
62 android:layout_height="wrap_content"
63 android:layout_marginHorizontal="24dp"
64 android:text="@string/about"
65 android:textAlignment="viewStart" />
66
67 <com.google.android.material.textview.MaterialTextView
68 style="@style/TextAppearance.Material3.BodyMedium"
69 android:layout_width="match_parent"
70 android:layout_height="wrap_content"
71 android:layout_marginHorizontal="24dp"
72 android:layout_marginTop="6dp"
73 android:text="@string/about_app_description"
74 android:textAlignment="viewStart" />
75
76 </LinearLayout>
77
78 <com.google.android.material.divider.MaterialDivider
79 android:layout_width="match_parent"
80 android:layout_height="wrap_content"
81 android:layout_marginHorizontal="20dp" />
82
83 <LinearLayout
84 android:id="@+id/button_contributors"
85 android:layout_width="match_parent"
86 android:layout_height="wrap_content"
87 android:background="?attr/selectableItemBackground"
88 android:orientation="vertical"
89 android:paddingHorizontal="16dp"
90 android:paddingVertical="16dp">
91
92 <com.google.android.material.textview.MaterialTextView
93 style="@style/TextAppearance.Material3.TitleMedium"
94 android:layout_width="match_parent"
95 android:layout_height="wrap_content"
96 android:layout_marginHorizontal="24dp"
97 android:text="@string/contributors"
98 android:textAlignment="viewStart" />
99
100 <com.google.android.material.textview.MaterialTextView
101 style="@style/TextAppearance.Material3.BodyMedium"
102 android:layout_width="match_parent"
103 android:layout_height="wrap_content"
104 android:layout_marginHorizontal="24dp"
105 android:layout_marginTop="6dp"
106 android:text="@string/contributors_description"
107 android:textAlignment="viewStart" />
108
109 </LinearLayout>
110
111 <com.google.android.material.divider.MaterialDivider
112 android:layout_width="match_parent"
113 android:layout_height="wrap_content"
114 android:layout_marginHorizontal="20dp" />
115
116 <LinearLayout
117 android:id="@+id/button_licenses"
118 android:layout_width="match_parent"
119 android:layout_height="wrap_content"
120 android:background="?attr/selectableItemBackground"
121 android:orientation="vertical"
122 android:paddingHorizontal="16dp"
123 android:paddingVertical="16dp">
124
125 <com.google.android.material.textview.MaterialTextView
126 style="@style/TextAppearance.Material3.TitleMedium"
127 android:layout_width="match_parent"
128 android:layout_height="wrap_content"
129 android:layout_marginHorizontal="24dp"
130 android:text="@string/licenses"
131 android:textAlignment="viewStart" />
132
133 <com.google.android.material.textview.MaterialTextView
134 style="@style/TextAppearance.Material3.BodyMedium"
135 android:layout_width="match_parent"
136 android:layout_height="wrap_content"
137 android:layout_marginHorizontal="24dp"
138 android:layout_marginTop="6dp"
139 android:text="@string/licenses_description"
140 android:textAlignment="viewStart" />
141
142 </LinearLayout>
143
144 <com.google.android.material.divider.MaterialDivider
145 android:layout_width="match_parent"
146 android:layout_height="wrap_content"
147 android:layout_marginHorizontal="20dp" />
148
149 <LinearLayout
150 android:id="@+id/button_build_hash"
151 android:layout_width="match_parent"
152 android:layout_height="wrap_content"
153 android:background="?attr/selectableItemBackground"
154 android:orientation="vertical"
155 android:paddingHorizontal="16dp"
156 android:paddingVertical="16dp">
157
158 <com.google.android.material.textview.MaterialTextView
159 style="@style/TextAppearance.Material3.TitleMedium"
160 android:layout_width="match_parent"
161 android:layout_height="wrap_content"
162 android:layout_marginHorizontal="24dp"
163 android:text="@string/build"
164 android:textAlignment="viewStart" />
165
166 <com.google.android.material.textview.MaterialTextView
167 android:id="@+id/text_build_hash"
168 style="@style/TextAppearance.Material3.BodyMedium"
169 android:layout_width="match_parent"
170 android:layout_height="wrap_content"
171 android:layout_marginHorizontal="24dp"
172 android:layout_marginTop="6dp"
173 android:textAlignment="viewStart"
174 tools:text="abc123" />
175
176 </LinearLayout>
177
178 <com.google.android.material.divider.MaterialDivider
179 android:layout_width="match_parent"
180 android:layout_height="wrap_content"
181 android:layout_marginHorizontal="20dp" />
182
183 <LinearLayout
184 android:layout_width="match_parent"
185 android:layout_height="wrap_content"
186 android:layout_marginHorizontal="40dp"
187 android:layout_marginTop="12dp"
188 android:layout_marginBottom="16dp"
189 android:gravity="center_horizontal"
190 android:orientation="horizontal">
191
192 <Button
193 android:id="@+id/button_discord"
194 style="?attr/materialIconButtonStyle"
195 android:layout_width="0dp"
196 android:layout_height="wrap_content"
197 android:layout_weight="1"
198 app:icon="@drawable/ic_discord"
199 app:iconGravity="textEnd"
200 app:iconSize="24dp"
201 app:iconTint="?attr/colorOnSurface" />
202
203 <Button
204 android:id="@+id/button_website"
205 style="?attr/materialIconButtonStyle"
206 android:layout_width="0dp"
207 android:layout_height="wrap_content"
208 android:layout_weight="1"
209 app:icon="@drawable/ic_website"
210 app:iconGravity="textEnd"
211 app:iconSize="24dp"
212 app:iconTint="?attr/colorOnSurface" />
213
214 <Button
215 android:id="@+id/button_github"
216 style="?attr/materialIconButtonStyle"
217 android:layout_width="0dp"
218 android:layout_height="wrap_content"
219 android:layout_weight="1"
220 app:icon="@drawable/ic_github"
221 app:iconGravity="textEnd"
222 app:iconSize="24dp"
223 app:iconTint="?attr/colorOnSurface" />
224
225 </LinearLayout>
226
227 </LinearLayout>
228
229 </LinearLayout>
230
231 </androidx.core.widget.NestedScrollView>
232
233</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/src/android/app/src/main/res/layout/card_home_option.xml b/src/android/app/src/main/res/layout/card_home_option.xml
index 6e8a232f9..cb667c928 100644
--- a/src/android/app/src/main/res/layout/card_home_option.xml
+++ b/src/android/app/src/main/res/layout/card_home_option.xml
@@ -6,8 +6,8 @@
6 android:id="@+id/option_card" 6 android:id="@+id/option_card"
7 android:layout_width="match_parent" 7 android:layout_width="match_parent"
8 android:layout_height="wrap_content" 8 android:layout_height="wrap_content"
9 android:layout_marginVertical="12dp" 9 android:layout_marginBottom="24dp"
10 android:layout_marginHorizontal="16dp" 10 android:layout_marginHorizontal="12dp"
11 android:background="?attr/selectableItemBackground" 11 android:background="?attr/selectableItemBackground"
12 android:backgroundTint="?attr/colorSurfaceVariant" 12 android:backgroundTint="?attr/colorSurfaceVariant"
13 android:clickable="true" 13 android:clickable="true"
diff --git a/src/android/app/src/main/res/layout/fragment_about.xml b/src/android/app/src/main/res/layout/fragment_about.xml
index 3e1d98451..a24f5230e 100644
--- a/src/android/app/src/main/res/layout/fragment_about.xml
+++ b/src/android/app/src/main/res/layout/fragment_about.xml
@@ -38,17 +38,17 @@
38 38
39 <ImageView 39 <ImageView
40 android:id="@+id/image_logo" 40 android:id="@+id/image_logo"
41 android:layout_width="250dp" 41 android:layout_width="150dp"
42 android:layout_height="250dp" 42 android:layout_height="150dp"
43 android:layout_marginTop="20dp" 43 android:layout_marginTop="24dp"
44 android:layout_marginBottom="28dp"
44 android:layout_gravity="center_horizontal" 45 android:layout_gravity="center_horizontal"
45 android:src="@drawable/ic_yuzu_title" /> 46 android:src="@drawable/ic_yuzu_title" />
46 47
47 <com.google.android.material.divider.MaterialDivider 48 <com.google.android.material.divider.MaterialDivider
48 android:layout_width="match_parent" 49 android:layout_width="match_parent"
49 android:layout_height="wrap_content" 50 android:layout_height="wrap_content"
50 android:layout_marginHorizontal="20dp" 51 android:layout_marginHorizontal="20dp" />
51 android:layout_marginTop="28dp" />
52 52
53 <LinearLayout 53 <LinearLayout
54 android:layout_width="match_parent" 54 android:layout_width="match_parent"
diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml
index cd6360b45..5252adf54 100644
--- a/src/android/app/src/main/res/layout/fragment_emulation.xml
+++ b/src/android/app/src/main/res/layout/fragment_emulation.xml
@@ -139,7 +139,7 @@
139 139
140 <com.google.android.material.textview.MaterialTextView 140 <com.google.android.material.textview.MaterialTextView
141 android:id="@+id/show_fps_text" 141 android:id="@+id/show_fps_text"
142 style="@style/TextAppearance.Material3.BodyMedium" 142 style="@style/TextAppearance.Material3.BodySmall"
143 android:layout_width="wrap_content" 143 android:layout_width="wrap_content"
144 android:layout_height="wrap_content" 144 android:layout_height="wrap_content"
145 android:layout_gravity="left" 145 android:layout_gravity="left"
@@ -147,7 +147,8 @@
147 android:focusable="false" 147 android:focusable="false"
148 android:paddingHorizontal="20dp" 148 android:paddingHorizontal="20dp"
149 android:textColor="@android:color/white" 149 android:textColor="@android:color/white"
150 android:textSize="12sp" 150 android:shadowColor="@android:color/black"
151 android:shadowRadius="3"
151 tools:ignore="RtlHardcoded" /> 152 tools:ignore="RtlHardcoded" />
152 153
153 </FrameLayout> 154 </FrameLayout>
diff --git a/src/android/app/src/main/res/layout/fragment_home_settings.xml b/src/android/app/src/main/res/layout/fragment_home_settings.xml
index 1cb421dcb..d84093ba3 100644
--- a/src/android/app/src/main/res/layout/fragment_home_settings.xml
+++ b/src/android/app/src/main/res/layout/fragment_home_settings.xml
@@ -14,13 +14,14 @@
14 android:layout_width="match_parent" 14 android:layout_width="match_parent"
15 android:layout_height="match_parent" 15 android:layout_height="match_parent"
16 android:orientation="vertical" 16 android:orientation="vertical"
17 android:background="?attr/colorSurface"> 17 android:background="?attr/colorSurface"
18 android:paddingHorizontal="8dp">
18 19
19 <ImageView 20 <ImageView
20 android:id="@+id/logo_image" 21 android:id="@+id/logo_image"
21 android:layout_width="128dp" 22 android:layout_width="96dp"
22 android:layout_height="128dp" 23 android:layout_height="96dp"
23 android:layout_margin="64dp" 24 android:layout_marginVertical="32dp"
24 android:layout_gravity="center_horizontal" 25 android:layout_gravity="center_horizontal"
25 android:src="@drawable/ic_yuzu_full" /> 26 android:src="@drawable/ic_yuzu_full" />
26 27
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/layout/list_item_setting.xml b/src/android/app/src/main/res/layout/list_item_setting.xml
index f1037a740..544280e75 100644
--- a/src/android/app/src/main/res/layout/list_item_setting.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting.xml
@@ -10,41 +10,59 @@
10 android:focusable="true" 10 android:focusable="true"
11 android:gravity="center_vertical" 11 android:gravity="center_vertical"
12 android:minHeight="72dp" 12 android:minHeight="72dp"
13 android:padding="@dimen/spacing_large"> 13 android:padding="16dp">
14 14
15 <LinearLayout 15 <LinearLayout
16 android:layout_width="match_parent" 16 android:layout_width="match_parent"
17 android:layout_height="wrap_content" 17 android:layout_height="wrap_content"
18 android:orientation="vertical"> 18 android:orientation="horizontal">
19 19
20 <com.google.android.material.textview.MaterialTextView 20 <ImageView
21 android:id="@+id/text_setting_name" 21 android:id="@+id/icon"
22 style="@style/TextAppearance.Material3.HeadlineMedium" 22 android:layout_width="24dp"
23 android:layout_width="match_parent" 23 android:layout_height="24dp"
24 android:layout_height="wrap_content" 24 android:layout_marginStart="8dp"
25 android:textAlignment="viewStart" 25 android:layout_marginEnd="24dp"
26 android:textSize="16sp" 26 android:layout_gravity="center_vertical"
27 app:lineHeight="22dp" 27 android:visibility="gone"
28 tools:text="Setting Name" /> 28 app:tint="?attr/colorOnSurface" />
29
30 <com.google.android.material.textview.MaterialTextView
31 android:id="@+id/text_setting_description"
32 style="@style/TextAppearance.Material3.BodySmall"
33 android:layout_width="match_parent"
34 android:layout_height="wrap_content"
35 android:layout_marginTop="@dimen/spacing_small"
36 android:textAlignment="viewStart"
37 tools:text="@string/app_disclaimer" />
38 29
39 <com.google.android.material.textview.MaterialTextView 30 <LinearLayout
40 android:id="@+id/text_setting_value"
41 style="@style/TextAppearance.Material3.LabelMedium"
42 android:layout_width="match_parent" 31 android:layout_width="match_parent"
43 android:layout_height="wrap_content" 32 android:layout_height="wrap_content"
44 android:layout_marginTop="@dimen/spacing_small" 33 android:orientation="vertical">
45 android:textAlignment="viewStart" 34
46 android:textStyle="bold" 35 <com.google.android.material.textview.MaterialTextView
47 tools:text="1x" /> 36 android:id="@+id/text_setting_name"
37 style="@style/TextAppearance.Material3.HeadlineMedium"
38 android:layout_width="match_parent"
39 android:layout_height="wrap_content"
40 android:textAlignment="viewStart"
41 android:textSize="17sp"
42 app:lineHeight="22dp"
43 tools:text="Setting Name" />
44
45 <com.google.android.material.textview.MaterialTextView
46 android:id="@+id/text_setting_description"
47 style="@style/TextAppearance.Material3.BodySmall"
48 android:layout_width="match_parent"
49 android:layout_height="wrap_content"
50 android:layout_marginTop="@dimen/spacing_small"
51 android:textAlignment="viewStart"
52 tools:text="@string/app_disclaimer" />
53
54 <com.google.android.material.textview.MaterialTextView
55 android:id="@+id/text_setting_value"
56 style="@style/TextAppearance.Material3.LabelMedium"
57 android:layout_width="match_parent"
58 android:layout_height="wrap_content"
59 android:layout_marginTop="@dimen/spacing_small"
60 android:textAlignment="viewStart"
61 android:textStyle="bold"
62 android:textSize="13sp"
63 tools:text="1x" />
64
65 </LinearLayout>
48 66
49 </LinearLayout> 67 </LinearLayout>
50 68
diff --git a/src/android/app/src/main/res/layout/list_item_setting_switch.xml b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
index a5767adee..a8f5aff78 100644
--- a/src/android/app/src/main/res/layout/list_item_setting_switch.xml
+++ b/src/android/app/src/main/res/layout/list_item_setting_switch.xml
@@ -8,9 +8,7 @@
8 android:clickable="true" 8 android:clickable="true"
9 android:focusable="true" 9 android:focusable="true"
10 android:minHeight="72dp" 10 android:minHeight="72dp"
11 android:paddingVertical="@dimen/spacing_large" 11 android:padding="16dp">
12 android:paddingStart="@dimen/spacing_large"
13 android:paddingEnd="24dp">
14 12
15 <com.google.android.material.materialswitch.MaterialSwitch 13 <com.google.android.material.materialswitch.MaterialSwitch
16 android:id="@+id/switch_widget" 14 android:id="@+id/switch_widget"
@@ -24,7 +22,7 @@
24 android:layout_height="wrap_content" 22 android:layout_height="wrap_content"
25 android:layout_alignParentTop="true" 23 android:layout_alignParentTop="true"
26 android:layout_centerVertical="true" 24 android:layout_centerVertical="true"
27 android:layout_marginEnd="@dimen/spacing_large" 25 android:layout_marginEnd="24dp"
28 android:layout_toStartOf="@+id/switch_widget" 26 android:layout_toStartOf="@+id/switch_widget"
29 android:gravity="center_vertical" 27 android:gravity="center_vertical"
30 android:orientation="vertical"> 28 android:orientation="vertical">
@@ -35,7 +33,7 @@
35 android:layout_width="wrap_content" 33 android:layout_width="wrap_content"
36 android:layout_height="wrap_content" 34 android:layout_height="wrap_content"
37 android:textAlignment="viewStart" 35 android:textAlignment="viewStart"
38 android:textSize="16sp" 36 android:textSize="17sp"
39 app:lineHeight="28dp" 37 app:lineHeight="28dp"
40 tools:text="@string/frame_limit_enable" /> 38 tools:text="@string/frame_limit_enable" />
41 39
diff --git a/src/android/app/src/main/res/layout/list_item_settings_header.xml b/src/android/app/src/main/res/layout/list_item_settings_header.xml
index cf85bc0da..21276b19e 100644
--- a/src/android/app/src/main/res/layout/list_item_settings_header.xml
+++ b/src/android/app/src/main/res/layout/list_item_settings_header.xml
@@ -7,7 +7,8 @@
7 android:layout_height="wrap_content" 7 android:layout_height="wrap_content"
8 android:layout_gravity="start|center_vertical" 8 android:layout_gravity="start|center_vertical"
9 android:paddingHorizontal="@dimen/spacing_large" 9 android:paddingHorizontal="@dimen/spacing_large"
10 android:paddingVertical="16dp" 10 android:paddingTop="16dp"
11 android:paddingBottom="8dp"
11 android:textAlignment="viewStart" 12 android:textAlignment="viewStart"
12 android:textColor="?attr/colorPrimary" 13 android:textColor="?attr/colorPrimary"
13 android:textStyle="bold" 14 android:textStyle="bold"
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index dc10159c9..51bcc49a3 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -2,7 +2,6 @@
2<resources> 2<resources>
3 3
4 <string-array name="regionNames"> 4 <string-array name="regionNames">
5 <item>@string/auto</item>
6 <item>@string/region_australia</item> 5 <item>@string/region_australia</item>
7 <item>@string/region_china</item> 6 <item>@string/region_china</item>
8 <item>@string/region_europe</item> 7 <item>@string/region_europe</item>
@@ -13,7 +12,6 @@
13 </string-array> 12 </string-array>
14 13
15 <integer-array name="regionValues"> 14 <integer-array name="regionValues">
16 <item>-1</item>
17 <item>3</item> 15 <item>3</item>
18 <item>4</item> 16 <item>4</item>
19 <item>2</item> 17 <item>2</item>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index c551a6106..471af8795 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -91,6 +91,7 @@
91 <string name="manage_save_data">Manage save data</string> 91 <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> 92 <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> 93 <string name="import_export_saves_description">Import or export save files</string>
94 <string name="save_files_exporting">Exporting save files…</string>
94 <string name="save_file_imported_success">Imported successfully</string> 95 <string name="save_file_imported_success">Imported successfully</string>
95 <string name="save_file_invalid_zip_structure">Invalid save directory structure</string> 96 <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> 97 <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@@ -240,6 +241,7 @@
240 <string name="shutting_down">Shutting down…</string> 241 <string name="shutting_down">Shutting down…</string>
241 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string> 242 <string name="reset_setting_confirmation">Do you want to reset this setting back to its default value?</string>
242 <string name="reset_to_default">Reset to default</string> 243 <string name="reset_to_default">Reset to default</string>
244 <string name="reset_to_default_description">Resets all advanced settings</string>
243 <string name="reset_all_settings">Reset all settings?</string> 245 <string name="reset_all_settings">Reset all settings?</string>
244 <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string> 246 <string name="reset_all_settings_description">All advanced settings will be reset to their default configuration. This can not be undone.</string>
245 <string name="settings_reset">Settings reset</string> 247 <string name="settings_reset">Settings reset</string>
@@ -255,6 +257,7 @@
255 <string name="cancelling">Cancelling</string> 257 <string name="cancelling">Cancelling</string>
256 <string name="install">Install</string> 258 <string name="install">Install</string>
257 <string name="delete">Delete</string> 259 <string name="delete">Delete</string>
260 <string name="export_success">Exported successfully</string>
258 261
259 <!-- GPU driver installation --> 262 <!-- GPU driver installation -->
260 <string name="select_gpu_driver">Select GPU driver</string> 263 <string name="select_gpu_driver">Select GPU driver</string>
@@ -271,10 +274,14 @@
271 <string name="preferences_settings">Settings</string> 274 <string name="preferences_settings">Settings</string>
272 <string name="preferences_general">General</string> 275 <string name="preferences_general">General</string>
273 <string name="preferences_system">System</string> 276 <string name="preferences_system">System</string>
277 <string name="preferences_system_description">Docked mode, region, language</string>
274 <string name="preferences_graphics">Graphics</string> 278 <string name="preferences_graphics">Graphics</string>
279 <string name="preferences_graphics_description">Accuracy level, resolution, shader cache</string>
275 <string name="preferences_audio">Audio</string> 280 <string name="preferences_audio">Audio</string>
281 <string name="preferences_audio_description">Output engine, volume</string>
276 <string name="preferences_theme">Theme and color</string> 282 <string name="preferences_theme">Theme and color</string>
277 <string name="preferences_debug">Debug</string> 283 <string name="preferences_debug">Debug</string>
284 <string name="preferences_debug_description">CPU/GPU debugging, graphics API, fastmem</string>
278 285
279 <!-- ROM loading errors --> 286 <!-- ROM loading errors -->
280 <string name="loader_error_encrypted">Your ROM is encrypted</string> 287 <string name="loader_error_encrypted">Your ROM is encrypted</string>
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
index 7f1ed0450..05cf3975d 100644
--- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
@@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) {
12} 12}
13 13
14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { 14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
15 return total_stream_count > 0 && stereo_stream_count > 0 && 15 return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); 16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
17} 17}
18} // namespace 18} // namespace
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp
index c6fd45f47..b7fed5304 100644
--- a/src/audio_core/opus/decoder.cpp
+++ b/src/audio_core/opus/decoder.cpp
@@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; 148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
149 OpusPacketHeader header{ReverseHeader(*header_p)}; 149 OpusPacketHeader header{ReverseHeader(*header_p)};
150 150
151 LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", 151 LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
152 header.size, input_data.size_bytes(), in_data.size_bytes()); 152 header.size, input_data.size_bytes(), in_data.size_bytes());
153 153
154 R_UNLESS(in_data.size_bytes() >= header.size && 154 R_UNLESS(in_data.size_bytes() >= header.size &&
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index bbb598bc5..51a23fe15 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -146,7 +146,7 @@ public:
146 return; 146 return;
147 } 147 }
148 148
149 paused = true; 149 SignalPause();
150 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) { 150 if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
151 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); 151 LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
152 } 152 }
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index 7b89151de..96e0efce2 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -111,7 +111,7 @@ public:
111 if (device == 0 || paused) { 111 if (device == 0 || paused) {
112 return; 112 return;
113 } 113 }
114 paused = true; 114 SignalPause();
115 SDL_PauseAudioDevice(device, 1); 115 SDL_PauseAudioDevice(device, 1);
116 } 116 }
117 117
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index d66d04fae..2a09db599 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -282,11 +282,19 @@ u64 SinkStream::GetExpectedPlayedSampleCount() {
282void SinkStream::WaitFreeSpace(std::stop_token stop_token) { 282void SinkStream::WaitFreeSpace(std::stop_token stop_token) {
283 std::unique_lock lk{release_mutex}; 283 std::unique_lock lk{release_mutex};
284 release_cv.wait_for(lk, std::chrono::milliseconds(5), 284 release_cv.wait_for(lk, std::chrono::milliseconds(5),
285 [this]() { return queued_buffers < max_queue_size; }); 285 [this]() { return paused || queued_buffers < max_queue_size; });
286 if (queued_buffers > max_queue_size + 3) { 286 if (queued_buffers > max_queue_size + 3) {
287 Common::CondvarWait(release_cv, lk, stop_token, 287 Common::CondvarWait(release_cv, lk, stop_token,
288 [this] { return queued_buffers < max_queue_size; }); 288 [this] { return paused || queued_buffers < max_queue_size; });
289 } 289 }
290} 290}
291 291
292void SinkStream::SignalPause() {
293 {
294 std::scoped_lock lk{release_mutex};
295 paused = true;
296 }
297 release_cv.notify_one();
298}
299
292} // namespace AudioCore::Sink 300} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 6a4996ca3..f2ccd19b8 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -214,6 +214,12 @@ public:
214 void WaitFreeSpace(std::stop_token stop_token); 214 void WaitFreeSpace(std::stop_token stop_token);
215 215
216protected: 216protected:
217 /**
218 * Unblocks the ADSP if the stream is paused.
219 */
220 void SignalPause();
221
222protected:
217 /// Core system 223 /// Core system
218 Core::System& system; 224 Core::System& system;
219 /// Type of this stream 225 /// Type of this stream
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 98b43e49c..a10131eb2 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -203,10 +203,12 @@ const char* TranslateCategory(Category category) {
203 case Category::Ui: 203 case Category::Ui:
204 case Category::UiGeneral: 204 case Category::UiGeneral:
205 return "UI"; 205 return "UI";
206 case Category::UiAudio:
207 return "UiAudio";
206 case Category::UiLayout: 208 case Category::UiLayout:
207 return "UiLayout"; 209 return "UILayout";
208 case Category::UiGameList: 210 case Category::UiGameList:
209 return "UiGameList"; 211 return "UIGameList";
210 case Category::Screenshots: 212 case Category::Screenshots:
211 return "Screenshots"; 213 return "Screenshots";
212 case Category::Shortcuts: 214 case Category::Shortcuts:
diff --git a/src/common/settings.h b/src/common/settings.h
index 0455241b3..b929fd957 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -153,7 +153,7 @@ struct Values {
153 true, 153 true,
154 true}; 154 true};
155 Setting<bool, false> audio_muted{ 155 Setting<bool, false> audio_muted{
156 linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; 156 linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true};
157 Setting<bool, false> dump_audio_commands{ 157 Setting<bool, false> dump_audio_commands{
158 linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; 158 linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false};
159 159
@@ -232,7 +232,11 @@ struct Values {
232 SwitchableSetting<bool> use_asynchronous_gpu_emulation{ 232 SwitchableSetting<bool> use_asynchronous_gpu_emulation{
233 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; 233 linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
234 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage, 234 SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
235#ifdef ANDROID
236 AstcDecodeMode::Cpu,
237#else
235 AstcDecodeMode::Gpu, 238 AstcDecodeMode::Gpu,
239#endif
236 AstcDecodeMode::Cpu, 240 AstcDecodeMode::Cpu,
237 AstcDecodeMode::CpuAsynchronous, 241 AstcDecodeMode::CpuAsynchronous,
238 "accelerate_astc", 242 "accelerate_astc",
@@ -304,7 +308,11 @@ struct Values {
304 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true}; 308 linkage, 0, "bg_blue", Category::Renderer, Specialization::Default, true, true};
305 309
306 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage, 310 SwitchableSetting<GpuAccuracy, true> gpu_accuracy{linkage,
311#ifdef ANDROID
312 GpuAccuracy::Normal,
313#else
307 GpuAccuracy::High, 314 GpuAccuracy::High,
315#endif
308 GpuAccuracy::Normal, 316 GpuAccuracy::Normal,
309 GpuAccuracy::Extreme, 317 GpuAccuracy::Extreme,
310 "gpu_accuracy", 318 "gpu_accuracy",
@@ -313,20 +321,38 @@ struct Values {
313 true, 321 true,
314 true}; 322 true};
315 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High}; 323 GpuAccuracy current_gpu_accuracy{GpuAccuracy::High};
316 SwitchableSetting<AnisotropyMode, true> max_anisotropy{ 324 SwitchableSetting<AnisotropyMode, true> max_anisotropy{linkage,
317 linkage, AnisotropyMode::Automatic, AnisotropyMode::Automatic, AnisotropyMode::X16, 325#ifdef ANDROID
318 "max_anisotropy", Category::RendererAdvanced}; 326 AnisotropyMode::Default,
327#else
328 AnisotropyMode::Automatic,
329#endif
330 AnisotropyMode::Automatic,
331 AnisotropyMode::X16,
332 "max_anisotropy",
333 Category::RendererAdvanced};
319 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage, 334 SwitchableSetting<AstcRecompression, true> astc_recompression{linkage,
320 AstcRecompression::Uncompressed, 335 AstcRecompression::Uncompressed,
321 AstcRecompression::Uncompressed, 336 AstcRecompression::Uncompressed,
322 AstcRecompression::Bc3, 337 AstcRecompression::Bc3,
323 "astc_recompression", 338 "astc_recompression",
324 Category::RendererAdvanced}; 339 Category::RendererAdvanced};
325 SwitchableSetting<bool> async_presentation{linkage, false, "async_presentation", 340 SwitchableSetting<bool> async_presentation{linkage,
326 Category::RendererAdvanced}; 341#ifdef ANDROID
342 true,
343#else
344 false,
345#endif
346 "async_presentation", Category::RendererAdvanced};
327 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock", 347 SwitchableSetting<bool> renderer_force_max_clock{linkage, false, "force_max_clock",
328 Category::RendererAdvanced}; 348 Category::RendererAdvanced};
329 SwitchableSetting<bool> use_reactive_flushing{linkage, true, "use_reactive_flushing", 349 SwitchableSetting<bool> use_reactive_flushing{linkage,
350#ifdef ANDROID
351 false,
352#else
353 true,
354#endif
355 "use_reactive_flushing",
330 Category::RendererAdvanced}; 356 Category::RendererAdvanced};
331 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders", 357 SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
332 Category::RendererAdvanced}; 358 Category::RendererAdvanced};
@@ -392,7 +418,11 @@ struct Values {
392 Setting<s32> current_user{linkage, 0, "current_user", Category::System}; 418 Setting<s32> current_user{linkage, 0, "current_user", Category::System};
393 419
394 SwitchableSetting<ConsoleMode> use_docked_mode{linkage, 420 SwitchableSetting<ConsoleMode> use_docked_mode{linkage,
421#ifdef ANDROID
422 ConsoleMode::Handheld,
423#else
395 ConsoleMode::Docked, 424 ConsoleMode::Docked,
425#endif
396 "use_docked_mode", 426 "use_docked_mode",
397 Category::System, 427 Category::System,
398 Specialization::Radio, 428 Specialization::Radio,
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index 1800ab10d..7943223eb 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -32,6 +32,7 @@ enum class Category : u32 {
32 AddOns, 32 AddOns,
33 Controls, 33 Controls,
34 Ui, 34 Ui,
35 UiAudio,
35 UiGeneral, 36 UiGeneral,
36 UiLayout, 37 UiLayout,
37 UiGameList, 38 UiGameList,
diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp
index 0a6eea3cf..a6007e7b2 100644
--- a/src/common/settings_input.cpp
+++ b/src/common/settings_input.cpp
@@ -6,10 +6,11 @@
6namespace Settings { 6namespace Settings {
7namespace NativeButton { 7namespace NativeButton {
8const std::array<const char*, NumButtons> mapping = {{ 8const std::array<const char*, NumButtons> mapping = {{
9 "button_a", "button_b", "button_x", "button_y", "button_lstick", 9 "button_a", "button_b", "button_x", "button_y", "button_lstick",
10 "button_rstick", "button_l", "button_r", "button_zl", "button_zr", 10 "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
11 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", 11 "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
12 "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", 12 "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot",
13 "button_slright", "button_srright",
13}}; 14}};
14} 15}
15 16
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 46f38c703..53a95ef8f 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -29,12 +29,15 @@ enum Values : int {
29 DRight, 29 DRight,
30 DDown, 30 DDown,
31 31
32 SL, 32 SLLeft,
33 SR, 33 SRLeft,
34 34
35 Home, 35 Home,
36 Screenshot, 36 Screenshot,
37 37
38 SLRight,
39 SRRight,
40
38 NumButtons, 41 NumButtons,
39}; 42};
40 43
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 8be3bdd08..66c10fc3f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -521,17 +521,28 @@ add_library(core STATIC
521 hle/service/grc/grc.h 521 hle/service/grc/grc.h
522 hle/service/hid/hid.cpp 522 hle/service/hid/hid.cpp
523 hle/service/hid/hid.h 523 hle/service/hid/hid.h
524 hle/service/hid/hid_debug_server.cpp
525 hle/service/hid/hid_debug_server.h
526 hle/service/hid/hid_firmware_settings.cpp
527 hle/service/hid/hid_firmware_settings.h
528 hle/service/hid/hid_server.cpp
529 hle/service/hid/hid_server.h
530 hle/service/hid/hid_system_server.cpp
531 hle/service/hid/hid_system_server.h
532 hle/service/hid/hid_util.h
524 hle/service/hid/hidbus.cpp 533 hle/service/hid/hidbus.cpp
525 hle/service/hid/hidbus.h 534 hle/service/hid/hidbus.h
526 hle/service/hid/irs.cpp 535 hle/service/hid/irs.cpp
527 hle/service/hid/irs.h 536 hle/service/hid/irs.h
528 hle/service/hid/irs_ring_lifo.h 537 hle/service/hid/irs_ring_lifo.h
538 hle/service/hid/resource_manager.cpp
539 hle/service/hid/resource_manager.h
529 hle/service/hid/ring_lifo.h 540 hle/service/hid/ring_lifo.h
530 hle/service/hid/xcd.cpp 541 hle/service/hid/xcd.cpp
531 hle/service/hid/xcd.h 542 hle/service/hid/xcd.h
532 hle/service/hid/errors.h 543 hle/service/hid/errors.h
533 hle/service/hid/controllers/console_sixaxis.cpp 544 hle/service/hid/controllers/console_six_axis.cpp
534 hle/service/hid/controllers/console_sixaxis.h 545 hle/service/hid/controllers/console_six_axis.h
535 hle/service/hid/controllers/controller_base.cpp 546 hle/service/hid/controllers/controller_base.cpp
536 hle/service/hid/controllers/controller_base.h 547 hle/service/hid/controllers/controller_base.h
537 hle/service/hid/controllers/debug_pad.cpp 548 hle/service/hid/controllers/debug_pad.cpp
@@ -546,6 +557,10 @@ add_library(core STATIC
546 hle/service/hid/controllers/npad.h 557 hle/service/hid/controllers/npad.h
547 hle/service/hid/controllers/palma.cpp 558 hle/service/hid/controllers/palma.cpp
548 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
549 hle/service/hid/controllers/stubbed.cpp 564 hle/service/hid/controllers/stubbed.cpp
550 hle/service/hid/controllers/stubbed.h 565 hle/service/hid/controllers/stubbed.h
551 hle/service/hid/controllers/touchscreen.cpp 566 hle/service/hid/controllers/touchscreen.cpp
@@ -715,6 +730,7 @@ add_library(core STATIC
715 hle/service/nvnflinger/producer_listener.h 730 hle/service/nvnflinger/producer_listener.h
716 hle/service/nvnflinger/status.h 731 hle/service/nvnflinger/status.h
717 hle/service/nvnflinger/ui/fence.h 732 hle/service/nvnflinger/ui/fence.h
733 hle/service/nvnflinger/ui/graphic_buffer.cpp
718 hle/service/nvnflinger/ui/graphic_buffer.h 734 hle/service/nvnflinger/ui/graphic_buffer.h
719 hle/service/nvnflinger/window.h 735 hle/service/nvnflinger/window.h
720 hle/service/olsc/olsc.cpp 736 hle/service/olsc/olsc.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 5e27dde58..558fba5bd 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -153,6 +153,14 @@ void ARM_Interface::Run() {
153 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())}; 153 Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
154 HaltReason hr{}; 154 HaltReason hr{};
155 155
156 // If the thread is scheduled for termination, exit the thread.
157 if (current_thread->HasDpc()) {
158 if (current_thread->IsTerminationRequested()) {
159 current_thread->Exit();
160 UNREACHABLE();
161 }
162 }
163
156 // Notify the debugger and go to sleep if a step was performed 164 // Notify the debugger and go to sleep if a step was performed
157 // and this thread has been scheduled again. 165 // and this thread has been scheduled again.
158 if (current_thread->GetStepState() == StepState::StepPerformed) { 166 if (current_thread->GetStepState() == StepState::StepPerformed) {
@@ -174,14 +182,6 @@ void ARM_Interface::Run() {
174 } 182 }
175 system.ExitCPUProfile(); 183 system.ExitCPUProfile();
176 184
177 // If the thread is scheduled for termination, exit the thread.
178 if (current_thread->HasDpc()) {
179 if (current_thread->IsTerminationRequested()) {
180 current_thread->Exit();
181 UNREACHABLE();
182 }
183 }
184
185 // Notify the debugger and go to sleep if a breakpoint was hit, 185 // Notify the debugger and go to sleep if a breakpoint was hit,
186 // or if the thread is unable to continue for any reason. 186 // or if the thread is unable to continue for any reason.
187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { 187 if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) {
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index e671b270f..d6b5abc68 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -76,6 +76,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
76} 76}
77 77
78void CoreTiming::ClearPendingEvents() { 78void CoreTiming::ClearPendingEvents() {
79 std::scoped_lock lock{basic_lock};
79 event_queue.clear(); 80 event_queue.clear();
80} 81}
81 82
@@ -113,6 +114,7 @@ bool CoreTiming::IsRunning() const {
113} 114}
114 115
115bool CoreTiming::HasPendingEvents() const { 116bool CoreTiming::HasPendingEvents() const {
117 std::scoped_lock lock{basic_lock};
116 return !(wait_set && event_queue.empty()); 118 return !(wait_set && event_queue.empty());
117} 119}
118 120
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 26a8b93a7..21548f0a9 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -161,7 +161,7 @@ private:
161 std::shared_ptr<EventType> ev_lost; 161 std::shared_ptr<EventType> ev_lost;
162 Common::Event event{}; 162 Common::Event event{};
163 Common::Event pause_event{}; 163 Common::Event pause_event{};
164 std::mutex basic_lock; 164 mutable std::mutex basic_lock;
165 std::mutex advance_lock; 165 std::mutex advance_lock;
166 std::unique_ptr<std::jthread> timer_thread; 166 std::unique_ptr<std::jthread> timer_thread;
167 std::atomic<bool> paused{}; 167 std::atomic<bool> paused{};
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 79114bb6d..fae15a556 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -38,14 +38,6 @@ using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
38using ConsoleMotionValues = ConsoleMotionInfo; 38using ConsoleMotionValues = ConsoleMotionInfo;
39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>; 39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
40 40
41struct TouchFinger {
42 u64 last_touch{};
43 Common::Point<float> position{};
44 u32 id{};
45 TouchAttribute attribute{};
46 bool pressed{};
47};
48
49// Contains all motion related data that is used on the services 41// Contains all motion related data that is used on the services
50struct ConsoleMotion { 42struct ConsoleMotion {
51 Common::Vec3f accel{}; 43 Common::Vec3f accel{};
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index b08a71446..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));
@@ -243,10 +244,12 @@ void EmulatedController::LoadTASParams() {
243 tas_button_params[Settings::NativeButton::DUp].Set("button", 13); 244 tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
244 tas_button_params[Settings::NativeButton::DRight].Set("button", 14); 245 tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
245 tas_button_params[Settings::NativeButton::DDown].Set("button", 15); 246 tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
246 tas_button_params[Settings::NativeButton::SL].Set("button", 16); 247 tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
247 tas_button_params[Settings::NativeButton::SR].Set("button", 17); 248 tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
248 tas_button_params[Settings::NativeButton::Home].Set("button", 18); 249 tas_button_params[Settings::NativeButton::Home].Set("button", 18);
249 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); 250 tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
251 tas_button_params[Settings::NativeButton::SLRight].Set("button", 20);
252 tas_button_params[Settings::NativeButton::SRRight].Set("button", 21);
250 253
251 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); 254 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
252 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 255 tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@@ -262,7 +265,7 @@ void EmulatedController::LoadTASParams() {
262} 265}
263 266
264void EmulatedController::LoadVirtualGamepadParams() { 267void EmulatedController::LoadVirtualGamepadParams() {
265 const auto player_index = NpadIdTypeToIndex(npad_id_type); 268 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
266 Common::ParamPackage common_params{}; 269 Common::ParamPackage common_params{};
267 common_params.Set("engine", "virtual_gamepad"); 270 common_params.Set("engine", "virtual_gamepad");
268 common_params.Set("port", static_cast<int>(player_index)); 271 common_params.Set("port", static_cast<int>(player_index));
@@ -296,10 +299,12 @@ void EmulatedController::LoadVirtualGamepadParams() {
296 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); 299 virtual_button_params[Settings::NativeButton::DUp].Set("button", 13);
297 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); 300 virtual_button_params[Settings::NativeButton::DRight].Set("button", 14);
298 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); 301 virtual_button_params[Settings::NativeButton::DDown].Set("button", 15);
299 virtual_button_params[Settings::NativeButton::SL].Set("button", 16); 302 virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16);
300 virtual_button_params[Settings::NativeButton::SR].Set("button", 17); 303 virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17);
301 virtual_button_params[Settings::NativeButton::Home].Set("button", 18); 304 virtual_button_params[Settings::NativeButton::Home].Set("button", 18);
302 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); 305 virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
306 virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20);
307 virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21);
303 308
304 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); 309 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
305 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); 310 virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
@@ -504,9 +509,11 @@ void EmulatedController::ReloadInput() {
504 }); 509 });
505 } 510 }
506 turbo_button_state = 0; 511 turbo_button_state = 0;
512 is_initalized = true;
507} 513}
508 514
509void EmulatedController::UnloadInput() { 515void EmulatedController::UnloadInput() {
516 is_initalized = false;
510 for (auto& button : button_devices) { 517 for (auto& button : button_devices) {
511 button.reset(); 518 button.reset();
512 } 519 }
@@ -611,7 +618,7 @@ bool EmulatedController::IsConfiguring() const {
611} 618}
612 619
613void EmulatedController::SaveCurrentConfig() { 620void EmulatedController::SaveCurrentConfig() {
614 const auto player_index = NpadIdTypeToIndex(npad_id_type); 621 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
615 auto& player = Settings::values.players.GetValue()[player_index]; 622 auto& player = Settings::values.players.GetValue()[player_index];
616 player.connected = is_connected; 623 player.connected = is_connected;
617 player.controller_type = MapNPadToSettingsType(npad_type); 624 player.controller_type = MapNPadToSettingsType(npad_type);
@@ -867,12 +874,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
867 controller.npad_button_state.down.Assign(current_status.value); 874 controller.npad_button_state.down.Assign(current_status.value);
868 controller.debug_pad_button_state.d_down.Assign(current_status.value); 875 controller.debug_pad_button_state.d_down.Assign(current_status.value);
869 break; 876 break;
870 case Settings::NativeButton::SL: 877 case Settings::NativeButton::SLLeft:
871 controller.npad_button_state.left_sl.Assign(current_status.value); 878 controller.npad_button_state.left_sl.Assign(current_status.value);
879 break;
880 case Settings::NativeButton::SLRight:
872 controller.npad_button_state.right_sl.Assign(current_status.value); 881 controller.npad_button_state.right_sl.Assign(current_status.value);
873 break; 882 break;
874 case Settings::NativeButton::SR: 883 case Settings::NativeButton::SRLeft:
875 controller.npad_button_state.left_sr.Assign(current_status.value); 884 controller.npad_button_state.left_sr.Assign(current_status.value);
885 break;
886 case Settings::NativeButton::SRRight:
876 controller.npad_button_state.right_sr.Assign(current_status.value); 887 controller.npad_button_state.right_sr.Assign(current_status.value);
877 break; 888 break;
878 case Settings::NativeButton::Home: 889 case Settings::NativeButton::Home:
@@ -1198,13 +1209,16 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1198} 1209}
1199 1210
1200bool 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 }
1201 if (device_index >= output_devices.size()) { 1215 if (device_index >= output_devices.size()) {
1202 return false; 1216 return false;
1203 } 1217 }
1204 if (!output_devices[device_index]) { 1218 if (!output_devices[device_index]) {
1205 return false; 1219 return false;
1206 } 1220 }
1207 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1221 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1208 const auto& player = Settings::values.players.GetValue()[player_index]; 1222 const auto& player = Settings::values.players.GetValue()[player_index];
1209 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f; 1223 const f32 strength = static_cast<f32>(player.vibration_strength) / 100.0f;
1210 1224
@@ -1230,9 +1244,13 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1230} 1244}
1231 1245
1232bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1246bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1233 const auto player_index = NpadIdTypeToIndex(npad_id_type); 1247 const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
1234 const auto& player = Settings::values.players.GetValue()[player_index]; 1248 const auto& player = Settings::values.players.GetValue()[player_index];
1235 1249
1250 if (!is_initalized) {
1251 return false;
1252 }
1253
1236 if (!player.vibration_enabled) { 1254 if (!player.vibration_enabled) {
1237 return false; 1255 return false;
1238 } 1256 }
@@ -1252,6 +1270,10 @@ Common::Input::DriverResult EmulatedController::SetPollingMode(
1252 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) { 1270 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1253 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);
1254 1272
1273 if (!is_initalized) {
1274 return Common::Input::DriverResult::InvalidHandle;
1275 }
1276
1255 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)];
1256 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)];
1257 auto& nfc_output_device = output_devices[3]; 1279 auto& nfc_output_device = output_devices[3];
@@ -1297,6 +1319,10 @@ bool EmulatedController::SetCameraFormat(
1297 Core::IrSensor::ImageTransferProcessorFormat camera_format) { 1319 Core::IrSensor::ImageTransferProcessorFormat camera_format) {
1298 LOG_INFO(Service_HID, "Set camera format {}", camera_format); 1320 LOG_INFO(Service_HID, "Set camera format {}", camera_format);
1299 1321
1322 if (!is_initalized) {
1323 return false;
1324 }
1325
1300 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)];
1301 auto& camera_output_device = output_devices[2]; 1327 auto& camera_output_device = output_devices[2];
1302 1328
@@ -1320,6 +1346,11 @@ void EmulatedController::SetRingParam(Common::ParamPackage param) {
1320} 1346}
1321 1347
1322bool EmulatedController::HasNfc() const { 1348bool EmulatedController::HasNfc() const {
1349
1350 if (!is_initalized) {
1351 return false;
1352 }
1353
1323 const auto& nfc_output_device = output_devices[3]; 1354 const auto& nfc_output_device = output_devices[3];
1324 1355
1325 switch (npad_type) { 1356 switch (npad_type) {
@@ -1357,6 +1388,10 @@ bool EmulatedController::RemoveNfcHandle() {
1357} 1388}
1358 1389
1359bool EmulatedController::StartNfcPolling() { 1390bool EmulatedController::StartNfcPolling() {
1391 if (!is_initalized) {
1392 return false;
1393 }
1394
1360 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)];
1361 auto& nfc_virtual_output_device = output_devices[3]; 1396 auto& nfc_virtual_output_device = output_devices[3];
1362 1397
@@ -1368,6 +1403,10 @@ bool EmulatedController::StartNfcPolling() {
1368} 1403}
1369 1404
1370bool EmulatedController::StopNfcPolling() { 1405bool EmulatedController::StopNfcPolling() {
1406 if (!is_initalized) {
1407 return false;
1408 }
1409
1371 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)];
1372 auto& nfc_virtual_output_device = output_devices[3]; 1411 auto& nfc_virtual_output_device = output_devices[3];
1373 1412
@@ -1379,6 +1418,10 @@ bool EmulatedController::StopNfcPolling() {
1379} 1418}
1380 1419
1381bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) { 1420bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1421 if (!is_initalized) {
1422 return false;
1423 }
1424
1382 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)];
1383 auto& nfc_virtual_output_device = output_devices[3]; 1426 auto& nfc_virtual_output_device = output_devices[3];
1384 1427
@@ -1391,6 +1434,10 @@ bool EmulatedController::ReadAmiiboData(std::vector<u8>& data) {
1391 1434
1392bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request, 1435bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& request,
1393 Common::Input::MifareRequest& out_data) { 1436 Common::Input::MifareRequest& out_data) {
1437 if (!is_initalized) {
1438 return false;
1439 }
1440
1394 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)];
1395 auto& nfc_virtual_output_device = output_devices[3]; 1442 auto& nfc_virtual_output_device = output_devices[3];
1396 1443
@@ -1403,6 +1450,10 @@ bool EmulatedController::ReadMifareData(const Common::Input::MifareRequest& requ
1403} 1450}
1404 1451
1405bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) { 1452bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& request) {
1453 if (!is_initalized) {
1454 return false;
1455 }
1456
1406 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)];
1407 auto& nfc_virtual_output_device = output_devices[3]; 1458 auto& nfc_virtual_output_device = output_devices[3];
1408 1459
@@ -1414,6 +1465,10 @@ bool EmulatedController::WriteMifareData(const Common::Input::MifareRequest& req
1414} 1465}
1415 1466
1416bool EmulatedController::WriteNfc(const std::vector<u8>& data) { 1467bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1468 if (!is_initalized) {
1469 return false;
1470 }
1471
1417 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)];
1418 auto& nfc_virtual_output_device = output_devices[3]; 1473 auto& nfc_virtual_output_device = output_devices[3];
1419 1474
@@ -1425,6 +1480,10 @@ bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
1425} 1480}
1426 1481
1427void EmulatedController::SetLedPattern() { 1482void EmulatedController::SetLedPattern() {
1483 if (!is_initalized) {
1484 return;
1485 }
1486
1428 for (auto& device : output_devices) { 1487 for (auto& device : output_devices) {
1429 if (!device) { 1488 if (!device) {
1430 continue; 1489 continue;
@@ -1640,7 +1699,7 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1640 } 1699 }
1641 if (is_connected) { 1700 if (is_connected) {
1642 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1701 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1643 NpadIdTypeToIndex(npad_id_type)); 1702 Service::HID::NpadIdTypeToIndex(npad_id_type));
1644 } 1703 }
1645 npad_type = npad_type_; 1704 npad_type = npad_type_;
1646} 1705}
@@ -1890,12 +1949,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const {
1890 case Settings::NativeButton::DDown: 1949 case Settings::NativeButton::DDown:
1891 button_mask.down.Assign(1); 1950 button_mask.down.Assign(1);
1892 break; 1951 break;
1893 case Settings::NativeButton::SL: 1952 case Settings::NativeButton::SLLeft:
1894 button_mask.left_sl.Assign(1); 1953 button_mask.left_sl.Assign(1);
1954 break;
1955 case Settings::NativeButton::SLRight:
1895 button_mask.right_sl.Assign(1); 1956 button_mask.right_sl.Assign(1);
1896 break; 1957 break;
1897 case Settings::NativeButton::SR: 1958 case Settings::NativeButton::SRLeft:
1898 button_mask.left_sr.Assign(1); 1959 button_mask.left_sr.Assign(1);
1960 break;
1961 case Settings::NativeButton::SRRight:
1899 button_mask.right_sr.Assign(1); 1962 button_mask.right_sr.Assign(1);
1900 break; 1963 break;
1901 default: 1964 default:
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 7ba75a50c..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,
@@ -356,6 +364,14 @@ struct TouchState {
356}; 364};
357static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); 365static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
358 366
367struct TouchFinger {
368 u64 last_touch{};
369 Common::Point<float> position{};
370 u32 id{};
371 TouchAttribute attribute{};
372 bool pressed{};
373};
374
359// This is nn::hid::TouchScreenConfigurationForNx 375// This is nn::hid::TouchScreenConfigurationForNx
360struct TouchScreenConfigurationForNx { 376struct TouchScreenConfigurationForNx {
361 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; 377 TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
@@ -583,6 +599,29 @@ struct SixAxisSensorIcInformation {
583static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8, 599static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
584 "SixAxisSensorIcInformation is an invalid size"); 600 "SixAxisSensorIcInformation is an invalid size");
585 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
586// This is nn::hid::VibrationDeviceHandle 625// This is nn::hid::VibrationDeviceHandle
587struct VibrationDeviceHandle { 626struct VibrationDeviceHandle {
588 NpadStyleIndex npad_type{NpadStyleIndex::None}; 627 NpadStyleIndex npad_type{NpadStyleIndex::None};
@@ -693,60 +732,4 @@ struct UniquePadId {
693}; 732};
694static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); 733static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size");
695 734
696/// Converts a NpadIdType to an array index.
697constexpr size_t NpadIdTypeToIndex(NpadIdType npad_id_type) {
698 switch (npad_id_type) {
699 case NpadIdType::Player1:
700 return 0;
701 case NpadIdType::Player2:
702 return 1;
703 case NpadIdType::Player3:
704 return 2;
705 case NpadIdType::Player4:
706 return 3;
707 case NpadIdType::Player5:
708 return 4;
709 case NpadIdType::Player6:
710 return 5;
711 case NpadIdType::Player7:
712 return 6;
713 case NpadIdType::Player8:
714 return 7;
715 case NpadIdType::Handheld:
716 return 8;
717 case NpadIdType::Other:
718 return 9;
719 default:
720 return 0;
721 }
722}
723
724/// Converts an array index to a NpadIdType
725constexpr NpadIdType IndexToNpadIdType(size_t index) {
726 switch (index) {
727 case 0:
728 return NpadIdType::Player1;
729 case 1:
730 return NpadIdType::Player2;
731 case 2:
732 return NpadIdType::Player3;
733 case 3:
734 return NpadIdType::Player4;
735 case 4:
736 return NpadIdType::Player5;
737 case 5:
738 return NpadIdType::Player6;
739 case 6:
740 return NpadIdType::Player7;
741 case 7:
742 return NpadIdType::Player8;
743 case 8:
744 return NpadIdType::Handheld;
745 case 9:
746 return NpadIdType::Other;
747 default:
748 return NpadIdType::Invalid;
749 }
750}
751
752} // namespace Core::HID 735} // namespace Core::HID
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 76d6b8ab0..a6bdd28f2 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -5,21 +5,22 @@
5#include "core/hid/hid_types.h" 5#include "core/hid/hid_types.h"
6#include "core/hid/input_interpreter.h" 6#include "core/hid/input_interpreter.h"
7#include "core/hle/service/hid/controllers/npad.h" 7#include "core/hle/service/hid/controllers/npad.h"
8#include "core/hle/service/hid/hid.h" 8#include "core/hle/service/hid/hid_server.h"
9#include "core/hle/service/hid/resource_manager.h"
9#include "core/hle/service/sm/sm.h" 10#include "core/hle/service/sm/sm.h"
10 11
11InputInterpreter::InputInterpreter(Core::System& system) 12InputInterpreter::InputInterpreter(Core::System& system)
12 : npad{system.ServiceManager() 13 : npad{system.ServiceManager()
13 .GetService<Service::HID::Hid>("hid") 14 .GetService<Service::HID::IHidServer>("hid")
14 ->GetAppletResource() 15 ->GetResourceManager()
15 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} { 16 ->GetNpad()} {
16 ResetButtonStates(); 17 ResetButtonStates();
17} 18}
18 19
19InputInterpreter::~InputInterpreter() = default; 20InputInterpreter::~InputInterpreter() = default;
20 21
21void InputInterpreter::PollInput() { 22void InputInterpreter::PollInput() {
22 const auto button_state = npad.GetAndResetPressState(); 23 const auto button_state = npad->GetAndResetPressState();
23 24
24 previous_index = current_index; 25 previous_index = current_index;
25 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/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 6a57ad55c..47dc8fd35 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -1617,9 +1617,9 @@ void KPageTableBase::RemapPageGroup(PageLinkedList* page_list, KProcessAddress a
1617 const KMemoryInfo info = it->GetMemoryInfo(); 1617 const KMemoryInfo info = it->GetMemoryInfo();
1618 1618
1619 // Determine the range to map. 1619 // Determine the range to map.
1620 KProcessAddress map_address = std::max(info.GetAddress(), GetInteger(start_address)); 1620 KProcessAddress map_address = std::max<u64>(info.GetAddress(), GetInteger(start_address));
1621 const KProcessAddress map_end_address = 1621 const KProcessAddress map_end_address =
1622 std::min(info.GetEndAddress(), GetInteger(end_address)); 1622 std::min<u64>(info.GetEndAddress(), GetInteger(end_address));
1623 ASSERT(map_end_address != map_address); 1623 ASSERT(map_end_address != map_address);
1624 1624
1625 // Determine if we should disable head merge. 1625 // Determine if we should disable head merge.
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..9d1960cb7 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 }
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/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index c58d67d7d..0bcd87062 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -8,12 +8,17 @@ namespace Service::HID {
8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} 8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
9ControllerBase::~ControllerBase() = default; 9ControllerBase::~ControllerBase() = default;
10 10
11void ControllerBase::ActivateController() { 11Result ControllerBase::Activate() {
12 if (is_activated) { 12 if (is_activated) {
13 return; 13 return ResultSuccess;
14 } 14 }
15 is_activated = true; 15 is_activated = true;
16 OnInit(); 16 OnInit();
17 return ResultSuccess;
18}
19
20Result ControllerBase::Activate(u64 aruid) {
21 return Activate();
17} 22}
18 23
19void ControllerBase::DeactivateController() { 24void ControllerBase::DeactivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index d6f7a5073..9a44ee41e 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h"
7 8
8namespace Core::Timing { 9namespace Core::Timing {
9class CoreTiming; 10class CoreTiming;
@@ -31,7 +32,8 @@ public:
31 // When the controller is requesting a motion update for the shared memory 32 // When the controller is requesting a motion update for the shared memory
32 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} 33 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
33 34
34 void ActivateController(); 35 Result Activate();
36 Result Activate(u64 aruid);
35 37
36 void DeactivateController(); 38 void DeactivateController();
37 39
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 21695bda2..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();
@@ -457,12 +406,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
457 pad_entry.l_stick = stick_state.left; 406 pad_entry.l_stick = stick_state.left;
458 } 407 }
459 408
460 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { 409 if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
410 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
461 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); 411 pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
462 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); 412 pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
463 } 413 }
464 414
465 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { 415 if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
416 controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
466 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); 417 pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
467 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); 418 pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
468 } 419 }
@@ -482,7 +433,7 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
482 } 433 }
483} 434}
484 435
485void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 436void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
486 if (!IsControllerActivated()) { 437 if (!IsControllerActivated()) {
487 return; 438 return;
488 } 439 }
@@ -612,134 +563,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
612 } 563 }
613} 564}
614 565
615void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) { 566void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
616 if (!IsControllerActivated()) {
617 return;
618 }
619
620 for (std::size_t i = 0; i < controller_data.size(); ++i) {
621 auto& controller = controller_data[i];
622
623 const auto& controller_type = controller.device->GetNpadStyleIndex();
624
625 if (controller_type == Core::HID::NpadStyleIndex::None ||
626 !controller.device->IsConnected()) {
627 continue;
628 }
629
630 auto* npad = controller.shared_memory;
631 const auto& motion_state = controller.device->GetMotions();
632 auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
633 auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
634 auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state;
635 auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state;
636 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
637 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
638
639 // Clear previous state
640 sixaxis_fullkey_state = {};
641 sixaxis_handheld_state = {};
642 sixaxis_dual_left_state = {};
643 sixaxis_dual_right_state = {};
644 sixaxis_left_lifo_state = {};
645 sixaxis_right_lifo_state = {};
646
647 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
648 controller.sixaxis_at_rest = true;
649 for (std::size_t e = 0; e < motion_state.size(); ++e) {
650 controller.sixaxis_at_rest =
651 controller.sixaxis_at_rest && motion_state[e].is_at_rest;
652 }
653 }
654
655 const auto set_motion_state = [&](SixAxisSensorState& state,
656 const Core::HID::ControllerMotion& hid_state) {
657 using namespace std::literals::chrono_literals;
658 static constexpr SixAxisSensorState default_motion_state = {
659 .delta_time = std::chrono::nanoseconds(5ms).count(),
660 .accel = {0, 0, -1.0f},
661 .orientation =
662 {
663 Common::Vec3f{1.0f, 0, 0},
664 Common::Vec3f{0, 1.0f, 0},
665 Common::Vec3f{0, 0, 1.0f},
666 },
667 .attribute = {1},
668 };
669 if (!controller.sixaxis_sensor_enabled) {
670 state = default_motion_state;
671 return;
672 }
673 if (!Settings::values.motion_enabled.GetValue()) {
674 state = default_motion_state;
675 return;
676 }
677 state.attribute.is_connected.Assign(1);
678 state.delta_time = std::chrono::nanoseconds(5ms).count();
679 state.accel = hid_state.accel;
680 state.gyro = hid_state.gyro;
681 state.rotation = hid_state.rotation;
682 state.orientation = hid_state.orientation;
683 };
684
685 switch (controller_type) {
686 case Core::HID::NpadStyleIndex::None:
687 ASSERT(false);
688 break;
689 case Core::HID::NpadStyleIndex::ProController:
690 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
691 break;
692 case Core::HID::NpadStyleIndex::Handheld:
693 set_motion_state(sixaxis_handheld_state, motion_state[0]);
694 break;
695 case Core::HID::NpadStyleIndex::JoyconDual:
696 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
697 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
698 break;
699 case Core::HID::NpadStyleIndex::JoyconLeft:
700 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
701 break;
702 case Core::HID::NpadStyleIndex::JoyconRight:
703 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
704 break;
705 case Core::HID::NpadStyleIndex::Pokeball:
706 using namespace std::literals::chrono_literals;
707 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
708 sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
709 break;
710 default:
711 break;
712 }
713
714 sixaxis_fullkey_state.sampling_number =
715 npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
716 sixaxis_handheld_state.sampling_number =
717 npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
718 sixaxis_dual_left_state.sampling_number =
719 npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
720 sixaxis_dual_right_state.sampling_number =
721 npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
722 sixaxis_left_lifo_state.sampling_number =
723 npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
724 sixaxis_right_lifo_state.sampling_number =
725 npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
726
727 if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
728 // This buffer only is updated on handheld on HW
729 npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
730 } else {
731 // Handheld doesn't update this buffer on HW
732 npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
733 }
734
735 npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
736 npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
737 npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
738 npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
739 }
740}
741
742void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
743 hid_core.SetSupportedStyleTag(style_set); 567 hid_core.SetSupportedStyleTag(style_set);
744 568
745 if (is_controller_initialized) { 569 if (is_controller_initialized) {
@@ -750,14 +574,14 @@ void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
750 is_controller_initialized = true; 574 is_controller_initialized = true;
751} 575}
752 576
753Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 577Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const {
754 if (!is_controller_initialized) { 578 if (!is_controller_initialized) {
755 return {Core::HID::NpadStyleSet::None}; 579 return {Core::HID::NpadStyleSet::None};
756 } 580 }
757 return hid_core.GetSupportedStyleTag(); 581 return hid_core.GetSupportedStyleTag();
758} 582}
759 583
760Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 584Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
761 constexpr std::size_t max_number_npad_ids = 0xa; 585 constexpr std::size_t max_number_npad_ids = 0xa;
762 const auto length = data.size(); 586 const auto length = data.size();
763 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 587 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
@@ -773,17 +597,17 @@ Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
773 return ResultSuccess; 597 return ResultSuccess;
774} 598}
775 599
776void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 600void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
777 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); 601 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32);
778 ASSERT(max_length <= copy_amount); 602 ASSERT(max_length <= copy_amount);
779 std::memcpy(data, supported_npad_id_types.data(), copy_amount); 603 std::memcpy(data, supported_npad_id_types.data(), copy_amount);
780} 604}
781 605
782std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const { 606std::size_t NPad::GetSupportedNpadIdTypesSize() const {
783 return supported_npad_id_types.size(); 607 return supported_npad_id_types.size();
784} 608}
785 609
786void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 610void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
787 if (joy_hold_type != NpadJoyHoldType::Horizontal && 611 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
788 joy_hold_type != NpadJoyHoldType::Vertical) { 612 joy_hold_type != NpadJoyHoldType::Vertical) {
789 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={}",
@@ -793,11 +617,11 @@ void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
793 hold_type = joy_hold_type; 617 hold_type = joy_hold_type;
794} 618}
795 619
796Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const { 620NPad::NpadJoyHoldType NPad::GetHoldType() const {
797 return hold_type; 621 return hold_type;
798} 622}
799 623
800void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 624void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
801 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { 625 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
802 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");
803 return; 627 return;
@@ -806,21 +630,20 @@ void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode a
806 handheld_activation_mode = activation_mode; 630 handheld_activation_mode = activation_mode;
807} 631}
808 632
809Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { 633NPad::NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const {
810 return handheld_activation_mode; 634 return handheld_activation_mode;
811} 635}
812 636
813void Controller_NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { 637void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) {
814 communication_mode = communication_mode_; 638 communication_mode = communication_mode_;
815} 639}
816 640
817Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode() const { 641NPad::NpadCommunicationMode NPad::GetNpadCommunicationMode() const {
818 return communication_mode; 642 return communication_mode;
819} 643}
820 644
821bool 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,
822 NpadJoyDeviceType npad_device_type, 646 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
823 NpadJoyAssignmentMode assignment_mode) {
824 if (!IsNpadIdValid(npad_id)) { 647 if (!IsNpadIdValid(npad_id)) {
825 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 648 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
826 return false; 649 return false;
@@ -889,9 +712,8 @@ bool Controller_NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID:
889 return true; 712 return true;
890} 713}
891 714
892bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 715bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
893 std::size_t device_index, 716 const Core::HID::VibrationValue& vibration_value) {
894 const Core::HID::VibrationValue& vibration_value) {
895 auto& controller = GetControllerFromNpadIdType(npad_id); 717 auto& controller = GetControllerFromNpadIdType(npad_id);
896 if (!controller.device->IsConnected()) { 718 if (!controller.device->IsConnected()) {
897 return false; 719 return false;
@@ -935,10 +757,9 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
935 return controller.device->SetVibration(device_index, vibration); 757 return controller.device->SetVibration(device_index, vibration);
936} 758}
937 759
938void Controller_NPad::VibrateController( 760void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle,
939 const Core::HID::VibrationDeviceHandle& vibration_device_handle, 761 const Core::HID::VibrationValue& vibration_value) {
940 const Core::HID::VibrationValue& vibration_value) { 762 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
941 if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
942 return; 763 return;
943 } 764 }
944 765
@@ -982,7 +803,7 @@ void Controller_NPad::VibrateController(
982 } 803 }
983} 804}
984 805
985void Controller_NPad::VibrateControllers( 806void NPad::VibrateControllers(
986 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 807 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
987 std::span<const Core::HID::VibrationValue> vibration_values) { 808 std::span<const Core::HID::VibrationValue> vibration_values) {
988 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 809 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
@@ -999,9 +820,9 @@ void Controller_NPad::VibrateControllers(
999 } 820 }
1000} 821}
1001 822
1002Core::HID::VibrationValue Controller_NPad::GetLastVibration( 823Core::HID::VibrationValue NPad::GetLastVibration(
1003 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 824 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1004 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 825 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1005 return {}; 826 return {};
1006 } 827 }
1007 828
@@ -1010,9 +831,9 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
1010 return controller.vibration[device_index].latest_vibration_value; 831 return controller.vibration[device_index].latest_vibration_value;
1011} 832}
1012 833
1013void Controller_NPad::InitializeVibrationDevice( 834void NPad::InitializeVibrationDevice(
1014 const Core::HID::VibrationDeviceHandle& vibration_device_handle) { 835 const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
1015 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 836 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1016 return; 837 return;
1017 } 838 }
1018 839
@@ -1021,8 +842,8 @@ void Controller_NPad::InitializeVibrationDevice(
1021 InitializeVibrationDeviceAtIndex(npad_index, device_index); 842 InitializeVibrationDeviceAtIndex(npad_index, device_index);
1022} 843}
1023 844
1024void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, 845void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id,
1025 std::size_t device_index) { 846 std::size_t device_index) {
1026 auto& controller = GetControllerFromNpadIdType(npad_id); 847 auto& controller = GetControllerFromNpadIdType(npad_id);
1027 if (!Settings::values.vibration_enabled.GetValue()) { 848 if (!Settings::values.vibration_enabled.GetValue()) {
1028 controller.vibration[device_index].device_mounted = false; 849 controller.vibration[device_index].device_mounted = false;
@@ -1033,13 +854,13 @@ void Controller_NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npa
1033 controller.device->IsVibrationEnabled(device_index); 854 controller.device->IsVibrationEnabled(device_index);
1034} 855}
1035 856
1036void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) { 857void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
1037 permit_vibration_session_enabled = permit_vibration_session; 858 permit_vibration_session_enabled = permit_vibration_session;
1038} 859}
1039 860
1040bool Controller_NPad::IsVibrationDeviceMounted( 861bool NPad::IsVibrationDeviceMounted(
1041 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 862 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
1042 if (IsDeviceHandleValid(vibration_device_handle).IsError()) { 863 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
1043 return false; 864 return false;
1044 } 865 }
1045 866
@@ -1048,7 +869,7 @@ bool Controller_NPad::IsVibrationDeviceMounted(
1048 return controller.vibration[device_index].device_mounted; 869 return controller.vibration[device_index].device_mounted;
1049} 870}
1050 871
1051Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { 872Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) {
1052 if (!IsNpadIdValid(npad_id)) { 873 if (!IsNpadIdValid(npad_id)) {
1053 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 874 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1054 // Fallback to player 1 875 // Fallback to player 1
@@ -1060,18 +881,17 @@ Kernel::KReadableEvent& Controller_NPad::GetStyleSetChangedEvent(Core::HID::Npad
1060 return controller.styleset_changed_event->GetReadableEvent(); 881 return controller.styleset_changed_event->GetReadableEvent();
1061} 882}
1062 883
1063void Controller_NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const { 884void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
1064 const auto& controller = GetControllerFromNpadIdType(npad_id); 885 const auto& controller = GetControllerFromNpadIdType(npad_id);
1065 controller.styleset_changed_event->Signal(); 886 controller.styleset_changed_event->Signal();
1066} 887}
1067 888
1068void Controller_NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, 889void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) {
1069 Core::HID::NpadIdType npad_id) {
1070 UpdateControllerAt(controller, npad_id, true); 890 UpdateControllerAt(controller, npad_id, true);
1071} 891}
1072 892
1073void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, 893void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id,
1074 Core::HID::NpadIdType npad_id, bool connected) { 894 bool connected) {
1075 auto& controller = GetControllerFromNpadIdType(npad_id); 895 auto& controller = GetControllerFromNpadIdType(npad_id);
1076 if (!connected) { 896 if (!connected) {
1077 DisconnectNpad(npad_id); 897 DisconnectNpad(npad_id);
@@ -1082,7 +902,7 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
1082 InitNewlyAddedController(npad_id); 902 InitNewlyAddedController(npad_id);
1083} 903}
1084 904
1085Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 905Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1086 if (!IsNpadIdValid(npad_id)) { 906 if (!IsNpadIdValid(npad_id)) {
1087 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 907 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1088 return InvalidNpadId; 908 return InvalidNpadId;
@@ -1131,54 +951,9 @@ Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1131 return ResultSuccess; 951 return ResultSuccess;
1132} 952}
1133 953
1134Result Controller_NPad::SetGyroscopeZeroDriftMode( 954Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1135 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1136 Core::HID::GyroscopeZeroDriftMode drift_mode) {
1137 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1138 if (is_valid.IsError()) {
1139 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1140 return is_valid;
1141 }
1142
1143 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1144 auto& controller = GetControllerFromHandle(sixaxis_handle);
1145 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1146 controller.device->SetGyroscopeZeroDriftMode(drift_mode);
1147
1148 return ResultSuccess;
1149}
1150
1151Result Controller_NPad::GetGyroscopeZeroDriftMode(
1152 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1153 Core::HID::GyroscopeZeroDriftMode& drift_mode) const {
1154 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1155 if (is_valid.IsError()) {
1156 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1157 return is_valid;
1158 }
1159
1160 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1161 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1162
1163 return ResultSuccess;
1164}
1165
1166Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1167 bool& is_at_rest) const {
1168 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1169 if (is_valid.IsError()) {
1170 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1171 return is_valid;
1172 }
1173
1174 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1175 is_at_rest = controller.sixaxis_at_rest;
1176 return ResultSuccess;
1177}
1178
1179Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1180 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { 955 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1181 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 956 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1182 if (is_valid.IsError()) { 957 if (is_valid.IsError()) {
1183 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 958 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1184 return is_valid; 959 return is_valid;
@@ -1189,65 +964,9 @@ Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1189 return ResultSuccess; 964 return ResultSuccess;
1190} 965}
1191 966
1192Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough( 967Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1193 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1194 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1195 if (is_valid.IsError()) {
1196 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1197 return is_valid;
1198 }
1199
1200 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1201 sixaxis.unaltered_passtrough = is_enabled;
1202 return ResultSuccess;
1203}
1204
1205Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1206 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1207 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1208 if (is_valid.IsError()) {
1209 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1210 return is_valid;
1211 }
1212
1213 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1214 is_enabled = sixaxis.unaltered_passtrough;
1215 return ResultSuccess;
1216}
1217
1218Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1219 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1220 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1221 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1222 if (is_valid.IsError()) {
1223 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1224 return is_valid;
1225 }
1226
1227 // TODO: Request this data to the controller. On error return 0xd8ca
1228 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1229 calibration = sixaxis.calibration;
1230 return ResultSuccess;
1231}
1232
1233Result Controller_NPad::GetSixAxisSensorIcInformation(
1234 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1235 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1236 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1237 if (is_valid.IsError()) {
1238 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1239 return is_valid;
1240 }
1241
1242 // TODO: Request this data to the controller. On error return 0xd8ca
1243 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1244 ic_information = sixaxis.ic_information;
1245 return ResultSuccess;
1246}
1247
1248Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1249 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 968 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1250 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle); 969 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
1251 if (is_valid.IsError()) { 970 if (is_valid.IsError()) {
1252 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 971 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1253 return is_valid; 972 return is_valid;
@@ -1259,83 +978,32 @@ Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1259 return ResultSuccess; 978 return ResultSuccess;
1260} 979}
1261 980
1262Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 981NPad::SixAxisLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) {
1263 bool sixaxis_status) { 982 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
1264 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1265 if (is_valid.IsError()) {
1266 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1267 return is_valid;
1268 }
1269
1270 auto& controller = GetControllerFromHandle(sixaxis_handle);
1271 controller.sixaxis_sensor_enabled = sixaxis_status;
1272 return ResultSuccess;
1273} 983}
1274 984
1275Result Controller_NPad::IsSixAxisSensorFusionEnabled( 985NPad::SixAxisLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
1276 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const { 986 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
1277 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1278 if (is_valid.IsError()) {
1279 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1280 return is_valid;
1281 }
1282
1283 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1284 is_fusion_enabled = sixaxis.is_fusion_enabled;
1285
1286 return ResultSuccess;
1287} 987}
1288Result Controller_NPad::SetSixAxisFusionEnabled(
1289 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1290 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1291 if (is_valid.IsError()) {
1292 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1293 return is_valid;
1294 }
1295 988
1296 auto& sixaxis = GetSixaxisState(sixaxis_handle); 989NPad::SixAxisLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
1297 sixaxis.is_fusion_enabled = is_fusion_enabled; 990 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
1298
1299 return ResultSuccess;
1300} 991}
1301 992
1302Result Controller_NPad::SetSixAxisFusionParameters( 993NPad::SixAxisLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
1303 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 994 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
1304 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1305 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1306 if (is_valid.IsError()) {
1307 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1308 return is_valid;
1309 }
1310
1311 const auto param1 = sixaxis_fusion_parameters.parameter1;
1312 if (param1 < 0.0f || param1 > 1.0f) {
1313 return InvalidSixAxisFusionRange;
1314 }
1315
1316 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1317 sixaxis.fusion = sixaxis_fusion_parameters;
1318
1319 return ResultSuccess;
1320} 995}
1321 996
1322Result Controller_NPad::GetSixAxisFusionParameters( 997NPad::SixAxisLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
1323 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 998 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
1324 Core::HID::SixAxisSensorFusionParameters& parameters) const { 999}
1325 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1326 if (is_valid.IsError()) {
1327 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1328 return is_valid;
1329 }
1330
1331 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1332 parameters = sixaxis.fusion;
1333 1000
1334 return ResultSuccess; 1001NPad::SixAxisLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1002 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1335} 1003}
1336 1004
1337Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1005Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1338 Core::HID::NpadIdType npad_id_2) { 1006 Core::HID::NpadIdType npad_id_2) {
1339 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1007 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1340 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,
1341 npad_id_2); 1009 npad_id_2);
@@ -1397,18 +1065,17 @@ Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1397 return ResultSuccess; 1065 return ResultSuccess;
1398} 1066}
1399 1067
1400void Controller_NPad::StartLRAssignmentMode() { 1068void NPad::StartLRAssignmentMode() {
1401 // 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
1402 // 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
1403 is_in_lr_assignment_mode = true; 1071 is_in_lr_assignment_mode = true;
1404} 1072}
1405 1073
1406void Controller_NPad::StopLRAssignmentMode() { 1074void NPad::StopLRAssignmentMode() {
1407 is_in_lr_assignment_mode = false; 1075 is_in_lr_assignment_mode = false;
1408} 1076}
1409 1077
1410Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1078Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) {
1411 Core::HID::NpadIdType npad_id_2) {
1412 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1079 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1413 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,
1414 npad_id_2); 1081 npad_id_2);
@@ -1439,8 +1106,7 @@ Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1439 return ResultSuccess; 1106 return ResultSuccess;
1440} 1107}
1441 1108
1442Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id, 1109Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
1443 Core::HID::LedPattern& pattern) const {
1444 if (!IsNpadIdValid(npad_id)) { 1110 if (!IsNpadIdValid(npad_id)) {
1445 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1111 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1446 return InvalidNpadId; 1112 return InvalidNpadId;
@@ -1450,8 +1116,8 @@ Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1450 return ResultSuccess; 1116 return ResultSuccess;
1451} 1117}
1452 1118
1453Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 1119Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
1454 bool& is_valid) const { 1120 bool& is_valid) const {
1455 if (!IsNpadIdValid(npad_id)) { 1121 if (!IsNpadIdValid(npad_id)) {
1456 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1122 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1457 return InvalidNpadId; 1123 return InvalidNpadId;
@@ -1461,8 +1127,8 @@ Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::
1461 return ResultSuccess; 1127 return ResultSuccess;
1462} 1128}
1463 1129
1464Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled( 1130Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
1465 bool is_protection_enabled, Core::HID::NpadIdType npad_id) { 1131 Core::HID::NpadIdType npad_id) {
1466 if (!IsNpadIdValid(npad_id)) { 1132 if (!IsNpadIdValid(npad_id)) {
1467 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1133 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1468 return InvalidNpadId; 1134 return InvalidNpadId;
@@ -1472,11 +1138,11 @@ Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1472 return ResultSuccess; 1138 return ResultSuccess;
1473} 1139}
1474 1140
1475void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1141void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
1476 analog_stick_use_center_clamp = use_center_clamp; 1142 analog_stick_use_center_clamp = use_center_clamp;
1477} 1143}
1478 1144
1479void Controller_NPad::ClearAllConnectedControllers() { 1145void NPad::ClearAllConnectedControllers() {
1480 for (auto& controller : controller_data) { 1146 for (auto& controller : controller_data) {
1481 if (controller.device->IsConnected() && 1147 if (controller.device->IsConnected() &&
1482 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { 1148 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
@@ -1486,13 +1152,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
1486 } 1152 }
1487} 1153}
1488 1154
1489void Controller_NPad::DisconnectAllConnectedControllers() { 1155void NPad::DisconnectAllConnectedControllers() {
1490 for (auto& controller : controller_data) { 1156 for (auto& controller : controller_data) {
1491 controller.device->Disconnect(); 1157 controller.device->Disconnect();
1492 } 1158 }
1493} 1159}
1494 1160
1495void Controller_NPad::ConnectAllDisconnectedControllers() { 1161void NPad::ConnectAllDisconnectedControllers() {
1496 for (auto& controller : controller_data) { 1162 for (auto& controller : controller_data) {
1497 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && 1163 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1498 !controller.device->IsConnected()) { 1164 !controller.device->IsConnected()) {
@@ -1501,18 +1167,18 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
1501 } 1167 }
1502} 1168}
1503 1169
1504void Controller_NPad::ClearAllControllers() { 1170void NPad::ClearAllControllers() {
1505 for (auto& controller : controller_data) { 1171 for (auto& controller : controller_data) {
1506 controller.device->Disconnect(); 1172 controller.device->Disconnect();
1507 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1173 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1508 } 1174 }
1509} 1175}
1510 1176
1511Core::HID::NpadButton Controller_NPad::GetAndResetPressState() { 1177Core::HID::NpadButton NPad::GetAndResetPressState() {
1512 return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); 1178 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1513} 1179}
1514 1180
1515void Controller_NPad::ApplyNpadSystemCommonPolicy() { 1181void NPad::ApplyNpadSystemCommonPolicy() {
1516 Core::HID::NpadStyleTag styletag{}; 1182 Core::HID::NpadStyleTag styletag{};
1517 styletag.fullkey.Assign(1); 1183 styletag.fullkey.Assign(1);
1518 styletag.handheld.Assign(1); 1184 styletag.handheld.Assign(1);
@@ -1537,7 +1203,7 @@ void Controller_NPad::ApplyNpadSystemCommonPolicy() {
1537 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld; 1203 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
1538} 1204}
1539 1205
1540bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const { 1206bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1541 if (controller == Core::HID::NpadStyleIndex::Handheld) { 1207 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1542 const bool support_handheld = 1208 const bool support_handheld =
1543 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(),
@@ -1588,51 +1254,50 @@ bool Controller_NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller
1588 return false; 1254 return false;
1589} 1255}
1590 1256
1591Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1257NPad::NpadControllerData& NPad::GetControllerFromHandle(
1592 const Core::HID::SixAxisSensorHandle& device_handle) { 1258 const Core::HID::VibrationDeviceHandle& device_handle) {
1593 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);
1594 return GetControllerFromNpadIdType(npad_id); 1260 return GetControllerFromNpadIdType(npad_id);
1595} 1261}
1596 1262
1597const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1263const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1598 const Core::HID::SixAxisSensorHandle& device_handle) const { 1264 const Core::HID::VibrationDeviceHandle& device_handle) const {
1599 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);
1600 return GetControllerFromNpadIdType(npad_id); 1266 return GetControllerFromNpadIdType(npad_id);
1601} 1267}
1602 1268
1603Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1269NPad::NpadControllerData& NPad::GetControllerFromHandle(
1604 const Core::HID::VibrationDeviceHandle& device_handle) { 1270 const Core::HID::SixAxisSensorHandle& device_handle) {
1605 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);
1606 return GetControllerFromNpadIdType(npad_id); 1272 return GetControllerFromNpadIdType(npad_id);
1607} 1273}
1608 1274
1609const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromHandle( 1275const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1610 const Core::HID::VibrationDeviceHandle& device_handle) const { 1276 const Core::HID::SixAxisSensorHandle& device_handle) const {
1611 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);
1612 return GetControllerFromNpadIdType(npad_id); 1278 return GetControllerFromNpadIdType(npad_id);
1613} 1279}
1614 1280
1615Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1281NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) {
1616 Core::HID::NpadIdType npad_id) {
1617 if (!IsNpadIdValid(npad_id)) { 1282 if (!IsNpadIdValid(npad_id)) {
1618 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1283 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1619 npad_id = Core::HID::NpadIdType::Player1; 1284 npad_id = Core::HID::NpadIdType::Player1;
1620 } 1285 }
1621 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1286 const auto npad_index = NpadIdTypeToIndex(npad_id);
1622 return controller_data[npad_index]; 1287 return controller_data[npad_index];
1623} 1288}
1624 1289
1625const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpadIdType( 1290const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
1626 Core::HID::NpadIdType npad_id) const { 1291 Core::HID::NpadIdType npad_id) const {
1627 if (!IsNpadIdValid(npad_id)) { 1292 if (!IsNpadIdValid(npad_id)) {
1628 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1293 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1629 npad_id = Core::HID::NpadIdType::Player1; 1294 npad_id = Core::HID::NpadIdType::Player1;
1630 } 1295 }
1631 const auto npad_index = Core::HID::NpadIdTypeToIndex(npad_id); 1296 const auto npad_index = NpadIdTypeToIndex(npad_id);
1632 return controller_data[npad_index]; 1297 return controller_data[npad_index];
1633} 1298}
1634 1299
1635Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1300Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1636 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1301 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1637 auto& controller = GetControllerFromHandle(sixaxis_handle); 1302 auto& controller = GetControllerFromHandle(sixaxis_handle);
1638 switch (sixaxis_handle.npad_type) { 1303 switch (sixaxis_handle.npad_type) {
@@ -1655,7 +1320,7 @@ Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1655 } 1320 }
1656} 1321}
1657 1322
1658const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties( 1323const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1659 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1324 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1660 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1325 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1661 switch (sixaxis_handle.npad_type) { 1326 switch (sixaxis_handle.npad_type) {
@@ -1678,50 +1343,13 @@ const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1678 } 1343 }
1679} 1344}
1680 1345
1681Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1346NPad::AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1682 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1347 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory;
1683 auto& controller = GetControllerFromHandle(sixaxis_handle);
1684 switch (sixaxis_handle.npad_type) {
1685 case Core::HID::NpadStyleIndex::ProController:
1686 case Core::HID::NpadStyleIndex::Pokeball:
1687 return controller.sixaxis_fullkey;
1688 case Core::HID::NpadStyleIndex::Handheld:
1689 return controller.sixaxis_handheld;
1690 case Core::HID::NpadStyleIndex::JoyconDual:
1691 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1692 return controller.sixaxis_dual_left;
1693 }
1694 return controller.sixaxis_dual_right;
1695 case Core::HID::NpadStyleIndex::JoyconLeft:
1696 return controller.sixaxis_left;
1697 case Core::HID::NpadStyleIndex::JoyconRight:
1698 return controller.sixaxis_right;
1699 default:
1700 return controller.sixaxis_unknown;
1701 }
1702}
1703 1348
1704const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( 1349 return {
1705 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1350 .ui_variant = 0,
1706 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1351 .footer = shared_memory->applet_footer_type,
1707 switch (sixaxis_handle.npad_type) { 1352 };
1708 case Core::HID::NpadStyleIndex::ProController:
1709 case Core::HID::NpadStyleIndex::Pokeball:
1710 return controller.sixaxis_fullkey;
1711 case Core::HID::NpadStyleIndex::Handheld:
1712 return controller.sixaxis_handheld;
1713 case Core::HID::NpadStyleIndex::JoyconDual:
1714 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1715 return controller.sixaxis_dual_left;
1716 }
1717 return controller.sixaxis_dual_right;
1718 case Core::HID::NpadStyleIndex::JoyconLeft:
1719 return controller.sixaxis_left;
1720 case Core::HID::NpadStyleIndex::JoyconRight:
1721 return controller.sixaxis_right;
1722 default:
1723 return controller.sixaxis_unknown;
1724 }
1725} 1353}
1726 1354
1727} // 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 949e58a4c..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,
@@ -86,6 +122,15 @@ public:
86 Default = 3, 122 Default = 3,
87 }; 123 };
88 124
125 enum class NpadRevision : u32 {
126 Revision0 = 0,
127 Revision1 = 1,
128 Revision2 = 2,
129 Revision3 = 3,
130 };
131
132 using SixAxisLifo = Lifo<Core::HID::SixAxisSensorState, hid_entry_count>;
133
89 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 134 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
90 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 135 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
91 136
@@ -138,37 +183,18 @@ public:
138 183
139 Result DisconnectNpad(Core::HID::NpadIdType npad_id); 184 Result DisconnectNpad(Core::HID::NpadIdType npad_id);
140 185
141 Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
142 Core::HID::GyroscopeZeroDriftMode drift_mode);
143 Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
144 Core::HID::GyroscopeZeroDriftMode& drift_mode) const;
145 Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
146 bool& is_at_rest) const;
147 Result IsFirmwareUpdateAvailableForSixAxisSensor( 186 Result IsFirmwareUpdateAvailableForSixAxisSensor(
148 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; 187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
149 Result EnableSixAxisSensorUnalteredPassthrough(
150 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
151 Result IsSixAxisSensorUnalteredPassthroughEnabled(
152 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
153 Result LoadSixAxisSensorCalibrationParameter(
154 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
155 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
156 Result GetSixAxisSensorIcInformation(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
158 Core::HID::SixAxisSensorIcInformation& ic_information) const;
159 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 188 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
160 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 189 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
161 Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 190
162 bool sixaxis_status); 191 SixAxisLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
163 Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 192 SixAxisLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
164 bool& is_fusion_enabled) const; 193 SixAxisLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
165 Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle, 194 SixAxisLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
166 bool is_fusion_enabled); 195 SixAxisLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
167 Result SetSixAxisFusionParameters( 196 SixAxisLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
168 const Core::HID::SixAxisSensorHandle& sixaxis_handle, 197
169 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
170 Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
171 Core::HID::SixAxisSensorFusionParameters& parameters) const;
172 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;
173 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 199 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
174 bool& is_enabled) const; 200 bool& is_enabled) const;
@@ -192,10 +218,7 @@ public:
192 218
193 void ApplyNpadSystemCommonPolicy(); 219 void ApplyNpadSystemCommonPolicy();
194 220
195 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 221 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
196 static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
197 static Result VerifyValidSixAxisSensorHandle(
198 const Core::HID::SixAxisSensorHandle& device_handle);
199 222
200private: 223private:
201 static constexpr std::size_t NPAD_COUNT = 10; 224 static constexpr std::size_t NPAD_COUNT = 10;
@@ -254,29 +277,6 @@ private:
254 }; 277 };
255 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 278 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
256 279
257 // This is nn::hid::SixAxisSensorAttribute
258 struct SixAxisSensorAttribute {
259 union {
260 u32 raw{};
261 BitField<0, 1, u32> is_connected;
262 BitField<1, 1, u32> is_interpolated;
263 };
264 };
265 static_assert(sizeof(SixAxisSensorAttribute) == 4, "SixAxisSensorAttribute is an invalid size");
266
267 // This is nn::hid::SixAxisSensorState
268 struct SixAxisSensorState {
269 s64 delta_time{};
270 s64 sampling_number{};
271 Common::Vec3f accel{};
272 Common::Vec3f gyro{};
273 Common::Vec3f rotation{};
274 std::array<Common::Vec3f, 3> orientation{};
275 SixAxisSensorAttribute attribute{};
276 INSERT_PADDING_BYTES(4); // Reserved
277 };
278 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
279
280 // This is nn::hid::server::NpadGcTriggerState 280 // This is nn::hid::server::NpadGcTriggerState
281 struct NpadGcTriggerState { 281 struct NpadGcTriggerState {
282 s64 sampling_number{}; 282 s64 sampling_number{};
@@ -353,37 +353,6 @@ private:
353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 353 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
354 "NfcXcdDeviceHandleStateImpl is an invalid size"); 354 "NfcXcdDeviceHandleStateImpl is an invalid size");
355 355
356 // This is nn::hid::system::AppletFooterUiAttributesSet
357 struct AppletFooterUiAttributes {
358 INSERT_PADDING_BYTES(0x4);
359 };
360
361 // This is nn::hid::system::AppletFooterUiType
362 enum class AppletFooterUiType : u8 {
363 None = 0,
364 HandheldNone = 1,
365 HandheldJoyConLeftOnly = 2,
366 HandheldJoyConRightOnly = 3,
367 HandheldJoyConLeftJoyConRight = 4,
368 JoyDual = 5,
369 JoyDualLeftOnly = 6,
370 JoyDualRightOnly = 7,
371 JoyLeftHorizontal = 8,
372 JoyLeftVertical = 9,
373 JoyRightHorizontal = 10,
374 JoyRightVertical = 11,
375 SwitchProController = 12,
376 CompatibleProController = 13,
377 CompatibleJoyCon = 14,
378 LarkHvc1 = 15,
379 LarkHvc2 = 16,
380 LarkNesLeft = 17,
381 LarkNesRight = 18,
382 Lucia = 19,
383 Verification = 20,
384 Lagon = 21,
385 };
386
387 // This is nn::hid::NpadLarkType 356 // This is nn::hid::NpadLarkType
388 enum class NpadLarkType : u32 { 357 enum class NpadLarkType : u32 {
389 Invalid, 358 Invalid,
@@ -427,12 +396,12 @@ private:
427 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{}; 396 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
428 Lifo<NPadGenericState, hid_entry_count> palma_lifo{}; 397 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
429 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{}; 398 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{}; 399 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{}; 400 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
432 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{}; 401 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
433 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{}; 402 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
434 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{}; 403 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
435 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{}; 404 Lifo<Core::HID::SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
436 DeviceType device_type{}; 405 DeviceType device_type{};
437 INSERT_PADDING_BYTES(0x4); // Reserved 406 INSERT_PADDING_BYTES(0x4); // Reserved
438 NPadSystemProperties system_properties{}; 407 NPadSystemProperties system_properties{};
@@ -466,16 +435,6 @@ private:
466 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 435 std::chrono::steady_clock::time_point last_vibration_timepoint{};
467 }; 436 };
468 437
469 struct SixaxisParameters {
470 bool is_fusion_enabled{true};
471 bool unaltered_passtrough{false};
472 Core::HID::SixAxisSensorFusionParameters fusion{};
473 Core::HID::SixAxisSensorCalibrationParameter calibration{};
474 Core::HID::SixAxisSensorIcInformation ic_information{};
475 Core::HID::GyroscopeZeroDriftMode gyroscope_zero_drift_mode{
476 Core::HID::GyroscopeZeroDriftMode::Standard};
477 };
478
479 struct NpadControllerData { 438 struct NpadControllerData {
480 Kernel::KEvent* styleset_changed_event{}; 439 Kernel::KEvent* styleset_changed_event{};
481 NpadInternalState* shared_memory = nullptr; 440 NpadInternalState* shared_memory = nullptr;
@@ -489,27 +448,10 @@ private:
489 bool is_dual_left_connected{true}; 448 bool is_dual_left_connected{true};
490 bool is_dual_right_connected{true}; 449 bool is_dual_right_connected{true};
491 450
492 // Motion parameters
493 bool sixaxis_at_rest{true};
494 bool sixaxis_sensor_enabled{true};
495 SixaxisParameters sixaxis_fullkey{};
496 SixaxisParameters sixaxis_handheld{};
497 SixaxisParameters sixaxis_dual_left{};
498 SixaxisParameters sixaxis_dual_right{};
499 SixaxisParameters sixaxis_left{};
500 SixaxisParameters sixaxis_right{};
501 SixaxisParameters sixaxis_unknown{};
502
503 // Current pad state 451 // Current pad state
504 NPadGenericState npad_pad_state{}; 452 NPadGenericState npad_pad_state{};
505 NPadGenericState npad_libnx_state{}; 453 NPadGenericState npad_libnx_state{};
506 NpadGcTriggerState npad_trigger_state{}; 454 NpadGcTriggerState npad_trigger_state{};
507 SixAxisSensorState sixaxis_fullkey_state{};
508 SixAxisSensorState sixaxis_handheld_state{};
509 SixAxisSensorState sixaxis_dual_left_state{};
510 SixAxisSensorState sixaxis_dual_right_state{};
511 SixAxisSensorState sixaxis_left_lifo_state{};
512 SixAxisSensorState sixaxis_right_lifo_state{};
513 int callback_key{}; 455 int callback_key{};
514 }; 456 };
515 457
@@ -520,13 +462,13 @@ private:
520 void WriteEmptyEntry(NpadInternalState* npad); 462 void WriteEmptyEntry(NpadInternalState* npad);
521 463
522 NpadControllerData& GetControllerFromHandle( 464 NpadControllerData& GetControllerFromHandle(
523 const Core::HID::SixAxisSensorHandle& device_handle);
524 const NpadControllerData& GetControllerFromHandle(
525 const Core::HID::SixAxisSensorHandle& device_handle) const;
526 NpadControllerData& GetControllerFromHandle(
527 const Core::HID::VibrationDeviceHandle& device_handle); 465 const Core::HID::VibrationDeviceHandle& device_handle);
528 const NpadControllerData& GetControllerFromHandle( 466 const NpadControllerData& GetControllerFromHandle(
529 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;
530 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 472 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
531 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 473 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
532 474
@@ -534,9 +476,6 @@ private:
534 const Core::HID::SixAxisSensorHandle& device_handle); 476 const Core::HID::SixAxisSensorHandle& device_handle);
535 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 477 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
536 const Core::HID::SixAxisSensorHandle& device_handle) const; 478 const Core::HID::SixAxisSensorHandle& device_handle) const;
537 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
538 const SixaxisParameters& GetSixaxisState(
539 const Core::HID::SixAxisSensorHandle& device_handle) const;
540 479
541 std::atomic<u64> press_state{}; 480 std::atomic<u64> press_state{};
542 481
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 73a2a2b91..588ff9d62 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -12,43 +12,43 @@
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 }
47 ActivateController(); 47 Activate();
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..3bcf0ee9f 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -15,8 +15,7 @@
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_)
20 : ControllerBase{hid_core_} { 19 : ControllerBase{hid_core_} {
21 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size, 20 static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
22 "TouchSharedMemory is bigger than the shared memory"); 21 "TouchSharedMemory is bigger than the shared memory");
@@ -25,13 +24,13 @@ Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
25 console = hid_core.GetEmulatedConsole(); 24 console = hid_core.GetEmulatedConsole();
26} 25}
27 26
28Controller_Touchscreen::~Controller_Touchscreen() = default; 27TouchScreen::~TouchScreen() = default;
29 28
30void Controller_Touchscreen::OnInit() {} 29void TouchScreen::OnInit() {}
31 30
32void Controller_Touchscreen::OnRelease() {} 31void TouchScreen::OnRelease() {}
33 32
34void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 33void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
35 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 34 shared_memory->touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
36 35
37 if (!IsControllerActivated()) { 36 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index dd00921fd..cd342ce91 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;
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.cpp b/src/core/hle/service/hid/hid.cpp
index 1d4101be9..1b7381d8d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1,2862 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/core.h"
9#include "core/core_timing.h"
10#include "core/hid/hid_core.h"
11#include "core/hle/kernel/k_readable_event.h"
12#include "core/hle/kernel/k_shared_memory.h"
13#include "core/hle/kernel/k_transfer_memory.h"
14#include "core/hle/kernel/kernel.h"
15#include "core/hle/service/hid/errors.h"
16#include "core/hle/service/hid/hid.h" 4#include "core/hle/service/hid/hid.h"
5#include "core/hle/service/hid/hid_debug_server.h"
6#include "core/hle/service/hid/hid_firmware_settings.h"
7#include "core/hle/service/hid/hid_server.h"
8#include "core/hle/service/hid/hid_system_server.h"
17#include "core/hle/service/hid/hidbus.h" 9#include "core/hle/service/hid/hidbus.h"
18#include "core/hle/service/hid/irs.h" 10#include "core/hle/service/hid/irs.h"
11#include "core/hle/service/hid/resource_manager.h"
19#include "core/hle/service/hid/xcd.h" 12#include "core/hle/service/hid/xcd.h"
20#include "core/hle/service/ipc_helpers.h"
21#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
22#include "core/memory.h"
23
24#include "core/hle/service/hid/controllers/console_sixaxis.h"
25#include "core/hle/service/hid/controllers/controller_base.h"
26#include "core/hle/service/hid/controllers/debug_pad.h"
27#include "core/hle/service/hid/controllers/gesture.h"
28#include "core/hle/service/hid/controllers/keyboard.h"
29#include "core/hle/service/hid/controllers/mouse.h"
30#include "core/hle/service/hid/controllers/npad.h"
31#include "core/hle/service/hid/controllers/palma.h"
32#include "core/hle/service/hid/controllers/stubbed.h"
33#include "core/hle/service/hid/controllers/touchscreen.h"
34#include "core/hle/service/hid/controllers/xpad.h"
35 14
36namespace Service::HID { 15namespace Service::HID {
37 16
38// Updating period for each HID device.
39// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
40// Correct npad_update_ns is 4ms this is overclocked to lower input lag
41constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
42constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
43constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
44constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
45
46IAppletResource::IAppletResource(Core::System& system_,
47 KernelHelpers::ServiceContext& service_context_)
48 : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} {
49 static const FunctionInfo functions[] = {
50 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
51 };
52 RegisterHandlers(functions);
53 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
54 MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
55 MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
56 MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
57 MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
58 MakeController<Controller_XPad>(HidController::XPad, shared_memory);
59 MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
60 MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
61 MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
62 MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
63 MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
64 MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
65 MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
66 MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
67 MakeController<Controller_Stubbed>(HidController::DebugMouse, shared_memory);
68 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
69
70 // Homebrew doesn't try to activate some controllers, so we activate them by default
71 GetController<Controller_NPad>(HidController::NPad).ActivateController();
72 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
73
74 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
75 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
76 GetController<Controller_Stubbed>(HidController::CaptureButton).SetCommonHeaderOffset(0x5000);
77 GetController<Controller_Stubbed>(HidController::InputDetector).SetCommonHeaderOffset(0x5200);
78 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
79 GetController<Controller_Stubbed>(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00);
80
81 // Register update callbacks
82 npad_update_event = Core::Timing::CreateEvent(
83 "HID::UpdatePadCallback",
84 [this](std::uintptr_t user_data, s64 time,
85 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
86 const auto guard = LockService();
87 UpdateNpad(user_data, ns_late);
88 return std::nullopt;
89 });
90 default_update_event = Core::Timing::CreateEvent(
91 "HID::UpdateDefaultCallback",
92 [this](std::uintptr_t user_data, s64 time,
93 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
94 const auto guard = LockService();
95 UpdateControllers(user_data, ns_late);
96 return std::nullopt;
97 });
98 mouse_keyboard_update_event = Core::Timing::CreateEvent(
99 "HID::UpdateMouseKeyboardCallback",
100 [this](std::uintptr_t user_data, s64 time,
101 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
102 const auto guard = LockService();
103 UpdateMouseKeyboard(user_data, ns_late);
104 return std::nullopt;
105 });
106 motion_update_event = Core::Timing::CreateEvent(
107 "HID::UpdateMotionCallback",
108 [this](std::uintptr_t user_data, s64 time,
109 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
110 const auto guard = LockService();
111 UpdateMotion(user_data, ns_late);
112 return std::nullopt;
113 });
114
115 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
116 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
117 default_update_event);
118 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
119 mouse_keyboard_update_event);
120 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
121 motion_update_event);
122
123 system.HIDCore().ReloadInputDevices();
124}
125
126void IAppletResource::ActivateController(HidController controller) {
127 controllers[static_cast<size_t>(controller)]->ActivateController();
128}
129
130void IAppletResource::DeactivateController(HidController controller) {
131 controllers[static_cast<size_t>(controller)]->DeactivateController();
132}
133
134IAppletResource::~IAppletResource() {
135 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
136 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
137 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
138 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
139}
140
141void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
142 LOG_DEBUG(Service_HID, "called");
143
144 IPC::ResponseBuilder rb{ctx, 2, 1};
145 rb.Push(ResultSuccess);
146 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
147}
148
149void IAppletResource::UpdateControllers(std::uintptr_t user_data,
150 std::chrono::nanoseconds ns_late) {
151 auto& core_timing = system.CoreTiming();
152
153 for (const auto& controller : controllers) {
154 // Keyboard has it's own update event
155 if (controller == controllers[static_cast<size_t>(HidController::Keyboard)]) {
156 continue;
157 }
158 // Mouse has it's own update event
159 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
160 continue;
161 }
162 // Npad has it's own update event
163 if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
164 continue;
165 }
166 controller->OnUpdate(core_timing);
167 }
168}
169
170void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
171 auto& core_timing = system.CoreTiming();
172
173 controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
174}
175
176void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
177 std::chrono::nanoseconds ns_late) {
178 auto& core_timing = system.CoreTiming();
179
180 controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
181 controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
182}
183
184void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
185 auto& core_timing = system.CoreTiming();
186
187 controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
188}
189
190class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
191public:
192 explicit IActiveVibrationDeviceList(Core::System& system_,
193 std::shared_ptr<IAppletResource> applet_resource_)
194 : ServiceFramework{system_, "IActiveVibrationDeviceList"},
195 applet_resource(applet_resource_) {
196 // clang-format off
197 static const FunctionInfo functions[] = {
198 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
199 };
200 // clang-format on
201
202 RegisterHandlers(functions);
203 }
204
205private:
206 void InitializeVibrationDevice(HLERequestContext& ctx) {
207 IPC::RequestParser rp{ctx};
208 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
209
210 if (applet_resource != nullptr) {
211 applet_resource->GetController<Controller_NPad>(HidController::NPad)
212 .InitializeVibrationDevice(vibration_device_handle);
213 }
214
215 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
216 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
217 vibration_device_handle.device_index);
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(ResultSuccess);
221 }
222
223 std::shared_ptr<IAppletResource> applet_resource;
224};
225
226std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
227 if (applet_resource == nullptr) {
228 applet_resource = std::make_shared<IAppletResource>(system, service_context);
229 }
230
231 return applet_resource;
232}
233
234Hid::Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
235 : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{
236 system_,
237 service_name} {
238 // clang-format off
239 static const FunctionInfo functions[] = {
240 {0, &Hid::CreateAppletResource, "CreateAppletResource"},
241 {1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
242 {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
243 {21, &Hid::ActivateMouse, "ActivateMouse"},
244 {26, nullptr, "ActivateDebugMouse"},
245 {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
246 {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
247 {40, nullptr, "AcquireXpadIdEventHandle"},
248 {41, nullptr, "ReleaseXpadIdEventHandle"},
249 {51, &Hid::ActivateXpad, "ActivateXpad"},
250 {55, &Hid::GetXpadIDs, "GetXpadIds"},
251 {56, nullptr, "ActivateJoyXpad"},
252 {58, nullptr, "GetJoyXpadLifoHandle"},
253 {59, nullptr, "GetJoyXpadIds"},
254 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
255 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
256 {62, nullptr, "GetSixAxisSensorLifoHandle"},
257 {63, nullptr, "ActivateJoySixAxisSensor"},
258 {64, nullptr, "DeactivateJoySixAxisSensor"},
259 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
260 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
261 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
262 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
263 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
264 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
265 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
266 {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
267 {73, nullptr, "SetAccelerometerParameters"},
268 {74, nullptr, "GetAccelerometerParameters"},
269 {75, nullptr, "ResetAccelerometerParameters"},
270 {76, nullptr, "SetAccelerometerPlayMode"},
271 {77, nullptr, "GetAccelerometerPlayMode"},
272 {78, nullptr, "ResetAccelerometerPlayMode"},
273 {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
274 {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
275 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
276 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
277 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
278 {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
279 {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
280 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
281 {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
282 {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
283 {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
284 {91, &Hid::ActivateGesture, "ActivateGesture"},
285 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
286 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
287 {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
288 {103, &Hid::ActivateNpad, "ActivateNpad"},
289 {104, &Hid::DeactivateNpad, "DeactivateNpad"},
290 {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
291 {107, &Hid::DisconnectNpad, "DisconnectNpad"},
292 {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
293 {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
294 {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
295 {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
296 {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
297 {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
298 {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
299 {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
300 {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"},
301 {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"},
302 {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
303 {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
304 {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
305 {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
306 {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
307 {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
308 {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
309 {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
310 {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
311 {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
312 {201, &Hid::SendVibrationValue, "SendVibrationValue"},
313 {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
314 {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
315 {204, &Hid::PermitVibration, "PermitVibration"},
316 {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"},
317 {206, &Hid::SendVibrationValues, "SendVibrationValues"},
318 {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
319 {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
320 {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
321 {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
322 {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
323 {212, nullptr, "SendVibrationValueInBool"},
324 {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
325 {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
326 {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
327 {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
328 {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
329 {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
330 {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
331 {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
332 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
333 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
334 {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
335 {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
336 {401, nullptr, "EnableUsbFullKeyController"},
337 {402, nullptr, "IsUsbFullKeyControllerConnected"},
338 {403, nullptr, "HasBattery"},
339 {404, nullptr, "HasLeftRightBattery"},
340 {405, nullptr, "GetNpadInterfaceType"},
341 {406, nullptr, "GetNpadLeftRightInterfaceType"},
342 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
343 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
344 {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
345 {501, &Hid::InitializePalma, "InitializePalma"},
346 {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
347 {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
348 {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"},
349 {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"},
350 {506, &Hid::ReadPalmaStep, "ReadPalmaStep"},
351 {507, &Hid::EnablePalmaStep, "EnablePalmaStep"},
352 {508, &Hid::ResetPalmaStep, "ResetPalmaStep"},
353 {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
354 {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
355 {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
356 {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
357 {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
358 {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
359 {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
360 {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
361 {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
362 {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"},
363 {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"},
364 {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
365 {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
366 {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
367 {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
368 {524, &Hid::PairPalma, "PairPalma"},
369 {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
370 {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
371 {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
372 {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
373 {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
374 {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
375 {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
376 {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
377 {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
378 {2000, nullptr, "ActivateDigitizer"},
379 };
380 // clang-format on
381
382 RegisterHandlers(functions);
383}
384
385Hid::~Hid() = default;
386
387void Hid::CreateAppletResource(HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto applet_resource_user_id{rp.Pop<u64>()};
390
391 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
392
393 if (applet_resource == nullptr) {
394 applet_resource = std::make_shared<IAppletResource>(system, service_context);
395 }
396
397 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
398 rb.Push(ResultSuccess);
399 rb.PushIpcInterface<IAppletResource>(applet_resource);
400}
401
402void Hid::ActivateDebugPad(HLERequestContext& ctx) {
403 IPC::RequestParser rp{ctx};
404 const auto applet_resource_user_id{rp.Pop<u64>()};
405
406 applet_resource->ActivateController(HidController::DebugPad);
407
408 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
409
410 IPC::ResponseBuilder rb{ctx, 2};
411 rb.Push(ResultSuccess);
412}
413
414void Hid::ActivateTouchScreen(HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto applet_resource_user_id{rp.Pop<u64>()};
417
418 applet_resource->ActivateController(HidController::Touchscreen);
419
420 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
421
422 IPC::ResponseBuilder rb{ctx, 2};
423 rb.Push(ResultSuccess);
424}
425
426void Hid::ActivateMouse(HLERequestContext& ctx) {
427 IPC::RequestParser rp{ctx};
428 const auto applet_resource_user_id{rp.Pop<u64>()};
429
430 applet_resource->ActivateController(HidController::Mouse);
431
432 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
433
434 IPC::ResponseBuilder rb{ctx, 2};
435 rb.Push(ResultSuccess);
436}
437
438void Hid::ActivateKeyboard(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440 const auto applet_resource_user_id{rp.Pop<u64>()};
441
442 applet_resource->ActivateController(HidController::Keyboard);
443
444 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
445
446 IPC::ResponseBuilder rb{ctx, 2};
447 rb.Push(ResultSuccess);
448}
449
450void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto flags{rp.Pop<u32>()};
453
454 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
455
456 IPC::ResponseBuilder rb{ctx, 2};
457 rb.Push(ResultSuccess);
458}
459
460void Hid::ActivateXpad(HLERequestContext& ctx) {
461 IPC::RequestParser rp{ctx};
462 struct Parameters {
463 u32 basic_xpad_id;
464 INSERT_PADDING_WORDS_NOINIT(1);
465 u64 applet_resource_user_id;
466 };
467 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
468
469 const auto parameters{rp.PopRaw<Parameters>()};
470
471 applet_resource->ActivateController(HidController::XPad);
472
473 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
474 parameters.basic_xpad_id, parameters.applet_resource_user_id);
475
476 IPC::ResponseBuilder rb{ctx, 2};
477 rb.Push(ResultSuccess);
478}
479
480void Hid::GetXpadIDs(HLERequestContext& ctx) {
481 IPC::RequestParser rp{ctx};
482 const auto applet_resource_user_id{rp.Pop<u64>()};
483
484 LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
485
486 IPC::ResponseBuilder rb{ctx, 3};
487 rb.Push(ResultSuccess);
488 rb.Push(0);
489}
490
491void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 u32 basic_xpad_id;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 // This function does nothing on 10.0.0+
503
504 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
505 parameters.basic_xpad_id, parameters.applet_resource_user_id);
506
507 IPC::ResponseBuilder rb{ctx, 2};
508 rb.Push(ResultSuccess);
509}
510
511void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) {
512 IPC::RequestParser rp{ctx};
513 struct Parameters {
514 u32 basic_xpad_id;
515 INSERT_PADDING_WORDS_NOINIT(1);
516 u64 applet_resource_user_id;
517 };
518 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
519
520 const auto parameters{rp.PopRaw<Parameters>()};
521
522 // This function does nothing on 10.0.0+
523
524 LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}",
525 parameters.basic_xpad_id, parameters.applet_resource_user_id);
526
527 IPC::ResponseBuilder rb{ctx, 2};
528 rb.Push(ResultSuccess);
529}
530
531void Hid::StartSixAxisSensor(HLERequestContext& ctx) {
532 IPC::RequestParser rp{ctx};
533 struct Parameters {
534 Core::HID::SixAxisSensorHandle sixaxis_handle;
535 INSERT_PADDING_WORDS_NOINIT(1);
536 u64 applet_resource_user_id;
537 };
538 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
539
540 const auto parameters{rp.PopRaw<Parameters>()};
541
542 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
543 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
544
545 LOG_DEBUG(Service_HID,
546 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
547 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
548 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
549
550 IPC::ResponseBuilder rb{ctx, 2};
551 rb.Push(result);
552}
553
554void Hid::StopSixAxisSensor(HLERequestContext& ctx) {
555 IPC::RequestParser rp{ctx};
556 struct Parameters {
557 Core::HID::SixAxisSensorHandle sixaxis_handle;
558 INSERT_PADDING_WORDS_NOINIT(1);
559 u64 applet_resource_user_id;
560 };
561 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
562
563 const auto parameters{rp.PopRaw<Parameters>()};
564
565 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
566 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
567
568 LOG_DEBUG(Service_HID,
569 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
570 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
571 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
572
573 IPC::ResponseBuilder rb{ctx, 2};
574 rb.Push(result);
575}
576
577void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
578 IPC::RequestParser rp{ctx};
579 struct Parameters {
580 Core::HID::SixAxisSensorHandle sixaxis_handle;
581 INSERT_PADDING_WORDS_NOINIT(1);
582 u64 applet_resource_user_id;
583 };
584 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
585
586 const auto parameters{rp.PopRaw<Parameters>()};
587
588 bool is_enabled{};
589 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
590 const auto result =
591 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
592
593 LOG_DEBUG(Service_HID,
594 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
595 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
596 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
597
598 IPC::ResponseBuilder rb{ctx, 3};
599 rb.Push(result);
600 rb.Push(is_enabled);
601}
602
603void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
604 IPC::RequestParser rp{ctx};
605 struct Parameters {
606 bool enable_sixaxis_sensor_fusion;
607 INSERT_PADDING_BYTES_NOINIT(3);
608 Core::HID::SixAxisSensorHandle sixaxis_handle;
609 u64 applet_resource_user_id;
610 };
611 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
612
613 const auto parameters{rp.PopRaw<Parameters>()};
614
615 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
616 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
617 parameters.enable_sixaxis_sensor_fusion);
618
619 LOG_DEBUG(Service_HID,
620 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
621 "device_index={}, applet_resource_user_id={}",
622 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
623 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
624 parameters.applet_resource_user_id);
625
626 IPC::ResponseBuilder rb{ctx, 2};
627 rb.Push(result);
628}
629
630void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
631 IPC::RequestParser rp{ctx};
632 struct Parameters {
633 Core::HID::SixAxisSensorHandle sixaxis_handle;
634 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
635 INSERT_PADDING_WORDS_NOINIT(1);
636 u64 applet_resource_user_id;
637 };
638 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
639
640 const auto parameters{rp.PopRaw<Parameters>()};
641
642 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
643 const auto result =
644 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
645
646 LOG_DEBUG(Service_HID,
647 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
648 "parameter2={}, applet_resource_user_id={}",
649 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
650 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
651 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
652
653 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(result);
655}
656
657void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
658 IPC::RequestParser rp{ctx};
659 struct Parameters {
660 Core::HID::SixAxisSensorHandle sixaxis_handle;
661 INSERT_PADDING_WORDS_NOINIT(1);
662 u64 applet_resource_user_id;
663 };
664 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
665
666 const auto parameters{rp.PopRaw<Parameters>()};
667
668 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
669 const auto& controller =
670 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
671 const auto result =
672 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
673
674 LOG_DEBUG(Service_HID,
675 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
676 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
677 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
678
679 IPC::ResponseBuilder rb{ctx, 4};
680 rb.Push(result);
681 rb.PushRaw(fusion_parameters);
682}
683
684void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
685 IPC::RequestParser rp{ctx};
686 struct Parameters {
687 Core::HID::SixAxisSensorHandle sixaxis_handle;
688 INSERT_PADDING_WORDS_NOINIT(1);
689 u64 applet_resource_user_id;
690 };
691 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
692
693 const auto parameters{rp.PopRaw<Parameters>()};
694
695 // Since these parameters are unknown just use what HW outputs
696 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
697 .parameter1 = 0.03f,
698 .parameter2 = 0.4f,
699 };
700 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
701 const auto result1 =
702 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
703 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
704
705 LOG_DEBUG(Service_HID,
706 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
707 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
708 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
709
710 IPC::ResponseBuilder rb{ctx, 2};
711 if (result1.IsError()) {
712 rb.Push(result1);
713 return;
714 }
715 rb.Push(result2);
716}
717
718void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
719 IPC::RequestParser rp{ctx};
720 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
721 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
722 const auto applet_resource_user_id{rp.Pop<u64>()};
723
724 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
725 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
726
727 LOG_DEBUG(Service_HID,
728 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
729 "applet_resource_user_id={}",
730 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
731 drift_mode, applet_resource_user_id);
732
733 IPC::ResponseBuilder rb{ctx, 2};
734 rb.Push(result);
735}
736
737void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
738 IPC::RequestParser rp{ctx};
739 struct Parameters {
740 Core::HID::SixAxisSensorHandle sixaxis_handle;
741 INSERT_PADDING_WORDS_NOINIT(1);
742 u64 applet_resource_user_id;
743 };
744 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
745
746 const auto parameters{rp.PopRaw<Parameters>()};
747
748 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
749 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
750 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
751
752 LOG_DEBUG(Service_HID,
753 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
754 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
755 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
756
757 IPC::ResponseBuilder rb{ctx, 3};
758 rb.Push(result);
759 rb.PushEnum(drift_mode);
760}
761
762void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
763 IPC::RequestParser rp{ctx};
764 struct Parameters {
765 Core::HID::SixAxisSensorHandle sixaxis_handle;
766 INSERT_PADDING_WORDS_NOINIT(1);
767 u64 applet_resource_user_id;
768 };
769 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
770
771 const auto parameters{rp.PopRaw<Parameters>()};
772
773 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
774 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
775 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
776
777 LOG_DEBUG(Service_HID,
778 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
779 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
780 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
781
782 IPC::ResponseBuilder rb{ctx, 2};
783 rb.Push(result);
784}
785
786void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
787 IPC::RequestParser rp{ctx};
788 struct Parameters {
789 Core::HID::SixAxisSensorHandle sixaxis_handle;
790 INSERT_PADDING_WORDS_NOINIT(1);
791 u64 applet_resource_user_id;
792 };
793 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
794
795 const auto parameters{rp.PopRaw<Parameters>()};
796
797 bool is_at_rest{};
798 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
799 controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
800
801 LOG_DEBUG(Service_HID,
802 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
803 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
804 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
805
806 IPC::ResponseBuilder rb{ctx, 3};
807 rb.Push(ResultSuccess);
808 rb.Push(is_at_rest);
809}
810
811void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
812 IPC::RequestParser rp{ctx};
813 struct Parameters {
814 Core::HID::SixAxisSensorHandle sixaxis_handle;
815 INSERT_PADDING_WORDS_NOINIT(1);
816 u64 applet_resource_user_id;
817 };
818 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
819
820 const auto parameters{rp.PopRaw<Parameters>()};
821
822 bool is_firmware_available{};
823 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
824 controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
825 is_firmware_available);
826
827 LOG_WARNING(
828 Service_HID,
829 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
830 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
831 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
832
833 IPC::ResponseBuilder rb{ctx, 3};
834 rb.Push(ResultSuccess);
835 rb.Push(is_firmware_available);
836}
837
838void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
839 IPC::RequestParser rp{ctx};
840 struct Parameters {
841 bool enabled;
842 Core::HID::SixAxisSensorHandle sixaxis_handle;
843 u64 applet_resource_user_id;
844 };
845 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
846
847 const auto parameters{rp.PopRaw<Parameters>()};
848
849 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
850 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
851 parameters.sixaxis_handle, parameters.enabled);
852
853 LOG_DEBUG(Service_HID,
854 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
855 "applet_resource_user_id={}",
856 parameters.enabled, parameters.sixaxis_handle.npad_type,
857 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
858 parameters.applet_resource_user_id);
859
860 IPC::ResponseBuilder rb{ctx, 2};
861 rb.Push(result);
862}
863
864void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
865 IPC::RequestParser rp{ctx};
866 struct Parameters {
867 Core::HID::SixAxisSensorHandle sixaxis_handle;
868 INSERT_PADDING_WORDS_NOINIT(1);
869 u64 applet_resource_user_id;
870 };
871 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
872
873 const auto parameters{rp.PopRaw<Parameters>()};
874
875 bool is_unaltered_sisxaxis_enabled{};
876 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
877 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
878 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
879
880 LOG_DEBUG(
881 Service_HID,
882 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
883 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
884 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
885
886 IPC::ResponseBuilder rb{ctx, 3};
887 rb.Push(result);
888 rb.Push(is_unaltered_sisxaxis_enabled);
889}
890
891void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
892 IPC::RequestParser rp{ctx};
893 struct Parameters {
894 Core::HID::SixAxisSensorHandle sixaxis_handle;
895 INSERT_PADDING_WORDS_NOINIT(1);
896 u64 applet_resource_user_id;
897 };
898 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
899
900 const auto parameters{rp.PopRaw<Parameters>()};
901
902 Core::HID::SixAxisSensorCalibrationParameter calibration{};
903 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
904 const auto result =
905 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
906
907 LOG_WARNING(
908 Service_HID,
909 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
910 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
911 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
912
913 if (result.IsSuccess()) {
914 ctx.WriteBuffer(calibration);
915 }
916
917 IPC::ResponseBuilder rb{ctx, 2};
918 rb.Push(result);
919}
920
921void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
922 IPC::RequestParser rp{ctx};
923 struct Parameters {
924 Core::HID::SixAxisSensorHandle sixaxis_handle;
925 INSERT_PADDING_WORDS_NOINIT(1);
926 u64 applet_resource_user_id;
927 };
928 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
929
930 const auto parameters{rp.PopRaw<Parameters>()};
931
932 Core::HID::SixAxisSensorIcInformation ic_information{};
933 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
934 const auto result =
935 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
936
937 LOG_WARNING(
938 Service_HID,
939 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
940 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
941 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
942
943 if (result.IsSuccess()) {
944 ctx.WriteBuffer(ic_information);
945 }
946
947 IPC::ResponseBuilder rb{ctx, 2};
948 rb.Push(result);
949}
950
951void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
952 IPC::RequestParser rp{ctx};
953 struct Parameters {
954 Core::HID::SixAxisSensorHandle sixaxis_handle;
955 INSERT_PADDING_WORDS_NOINIT(1);
956 u64 applet_resource_user_id;
957 };
958 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
959
960 const auto parameters{rp.PopRaw<Parameters>()};
961
962 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
963 const auto result =
964 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
965
966 LOG_WARNING(
967 Service_HID,
968 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
969 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
970 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
971
972 IPC::ResponseBuilder rb{ctx, 2};
973 rb.Push(result);
974}
975
976void Hid::ActivateGesture(HLERequestContext& ctx) {
977 IPC::RequestParser rp{ctx};
978 struct Parameters {
979 u32 unknown;
980 INSERT_PADDING_WORDS_NOINIT(1);
981 u64 applet_resource_user_id;
982 };
983 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
984
985 const auto parameters{rp.PopRaw<Parameters>()};
986
987 applet_resource->ActivateController(HidController::Gesture);
988
989 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
990 parameters.unknown, parameters.applet_resource_user_id);
991
992 IPC::ResponseBuilder rb{ctx, 2};
993 rb.Push(ResultSuccess);
994}
995
996void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 struct Parameters {
999 Core::HID::NpadStyleSet supported_styleset;
1000 INSERT_PADDING_WORDS_NOINIT(1);
1001 u64 applet_resource_user_id;
1002 };
1003 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1004
1005 const auto parameters{rp.PopRaw<Parameters>()};
1006
1007 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1008 .SetSupportedStyleSet({parameters.supported_styleset});
1009
1010 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
1011 parameters.supported_styleset, parameters.applet_resource_user_id);
1012
1013 IPC::ResponseBuilder rb{ctx, 2};
1014 rb.Push(ResultSuccess);
1015}
1016
1017void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
1018 IPC::RequestParser rp{ctx};
1019 const auto applet_resource_user_id{rp.Pop<u64>()};
1020
1021 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1022
1023 IPC::ResponseBuilder rb{ctx, 3};
1024 rb.Push(ResultSuccess);
1025 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1026 .GetSupportedStyleSet()
1027 .raw);
1028}
1029
1030void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) {
1031 IPC::RequestParser rp{ctx};
1032 const auto applet_resource_user_id{rp.Pop<u64>()};
1033
1034 const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1035 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1036
1037 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1038
1039 IPC::ResponseBuilder rb{ctx, 2};
1040 rb.Push(result);
1041}
1042
1043void Hid::ActivateNpad(HLERequestContext& ctx) {
1044 IPC::RequestParser rp{ctx};
1045 const auto applet_resource_user_id{rp.Pop<u64>()};
1046
1047 applet_resource->ActivateController(HidController::NPad);
1048
1049 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1050
1051 IPC::ResponseBuilder rb{ctx, 2};
1052 rb.Push(ResultSuccess);
1053}
1054
1055void Hid::DeactivateNpad(HLERequestContext& ctx) {
1056 IPC::RequestParser rp{ctx};
1057 const auto applet_resource_user_id{rp.Pop<u64>()};
1058
1059 applet_resource->DeactivateController(HidController::NPad);
1060
1061 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1062
1063 IPC::ResponseBuilder rb{ctx, 2};
1064 rb.Push(ResultSuccess);
1065}
1066
1067void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1068 IPC::RequestParser rp{ctx};
1069 struct Parameters {
1070 Core::HID::NpadIdType npad_id;
1071 INSERT_PADDING_WORDS_NOINIT(1);
1072 u64 applet_resource_user_id;
1073 u64 unknown;
1074 };
1075 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1076
1077 const auto parameters{rp.PopRaw<Parameters>()};
1078
1079 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1080 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1081
1082 // Games expect this event to be signaled after calling this function
1083 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1084 .SignalStyleSetChangedEvent(parameters.npad_id);
1085
1086 IPC::ResponseBuilder rb{ctx, 2, 1};
1087 rb.Push(ResultSuccess);
1088 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1089 .GetStyleSetChangedEvent(parameters.npad_id));
1090}
1091
1092void Hid::DisconnectNpad(HLERequestContext& ctx) {
1093 IPC::RequestParser rp{ctx};
1094 struct Parameters {
1095 Core::HID::NpadIdType npad_id;
1096 INSERT_PADDING_WORDS_NOINIT(1);
1097 u64 applet_resource_user_id;
1098 };
1099 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1100
1101 const auto parameters{rp.PopRaw<Parameters>()};
1102
1103 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1104 controller.DisconnectNpad(parameters.npad_id);
1105
1106 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1107 parameters.applet_resource_user_id);
1108
1109 IPC::ResponseBuilder rb{ctx, 2};
1110 rb.Push(ResultSuccess);
1111}
1112
1113void Hid::GetPlayerLedPattern(HLERequestContext& ctx) {
1114 IPC::RequestParser rp{ctx};
1115 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1116
1117 Core::HID::LedPattern pattern{0, 0, 0, 0};
1118 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1119 const auto result = controller.GetLedPattern(npad_id, pattern);
1120
1121 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1122
1123 IPC::ResponseBuilder rb{ctx, 4};
1124 rb.Push(result);
1125 rb.Push(pattern.raw);
1126}
1127
1128void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) {
1129 // Should have no effect with how our npad sets up the data
1130 IPC::RequestParser rp{ctx};
1131 struct Parameters {
1132 s32 revision;
1133 INSERT_PADDING_WORDS_NOINIT(1);
1134 u64 applet_resource_user_id;
1135 };
1136 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1137
1138 const auto parameters{rp.PopRaw<Parameters>()};
1139
1140 applet_resource->ActivateController(HidController::NPad);
1141
1142 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1143 parameters.applet_resource_user_id);
1144
1145 IPC::ResponseBuilder rb{ctx, 2};
1146 rb.Push(ResultSuccess);
1147}
1148
1149void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) {
1150 IPC::RequestParser rp{ctx};
1151 const auto applet_resource_user_id{rp.Pop<u64>()};
1152 const auto hold_type{rp.PopEnum<Controller_NPad::NpadJoyHoldType>()};
1153
1154 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
1155
1156 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1157 applet_resource_user_id, hold_type);
1158
1159 IPC::ResponseBuilder rb{ctx, 2};
1160 rb.Push(ResultSuccess);
1161}
1162
1163void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) {
1164 IPC::RequestParser rp{ctx};
1165 const auto applet_resource_user_id{rp.Pop<u64>()};
1166
1167 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1168
1169 IPC::ResponseBuilder rb{ctx, 4};
1170 rb.Push(ResultSuccess);
1171 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
1172}
1173
1174void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1175 IPC::RequestParser rp{ctx};
1176 struct Parameters {
1177 Core::HID::NpadIdType npad_id;
1178 INSERT_PADDING_WORDS_NOINIT(1);
1179 u64 applet_resource_user_id;
1180 };
1181 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1182
1183 const auto parameters{rp.PopRaw<Parameters>()};
1184
1185 Core::HID::NpadIdType new_npad_id{};
1186 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1187 controller.SetNpadMode(new_npad_id, parameters.npad_id,
1188 Controller_NPad::NpadJoyDeviceType::Left,
1189 Controller_NPad::NpadJoyAssignmentMode::Single);
1190
1191 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1192 parameters.applet_resource_user_id);
1193
1194 IPC::ResponseBuilder rb{ctx, 2};
1195 rb.Push(ResultSuccess);
1196}
1197
1198void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1199 IPC::RequestParser rp{ctx};
1200 struct Parameters {
1201 Core::HID::NpadIdType npad_id;
1202 INSERT_PADDING_WORDS_NOINIT(1);
1203 u64 applet_resource_user_id;
1204 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1205 };
1206 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1207
1208 const auto parameters{rp.PopRaw<Parameters>()};
1209
1210 Core::HID::NpadIdType new_npad_id{};
1211 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1212 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1213 Controller_NPad::NpadJoyAssignmentMode::Single);
1214
1215 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1216 parameters.npad_id, parameters.applet_resource_user_id,
1217 parameters.npad_joy_device_type);
1218
1219 IPC::ResponseBuilder rb{ctx, 2};
1220 rb.Push(ResultSuccess);
1221}
1222
1223void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1224 IPC::RequestParser rp{ctx};
1225 struct Parameters {
1226 Core::HID::NpadIdType npad_id;
1227 INSERT_PADDING_WORDS_NOINIT(1);
1228 u64 applet_resource_user_id;
1229 };
1230 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1231
1232 const auto parameters{rp.PopRaw<Parameters>()};
1233
1234 Core::HID::NpadIdType new_npad_id{};
1235 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1236 controller.SetNpadMode(new_npad_id, parameters.npad_id, {},
1237 Controller_NPad::NpadJoyAssignmentMode::Dual);
1238
1239 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1240 parameters.applet_resource_user_id);
1241
1242 IPC::ResponseBuilder rb{ctx, 2};
1243 rb.Push(ResultSuccess);
1244}
1245
1246void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1247 IPC::RequestParser rp{ctx};
1248 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1249 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1250 const auto applet_resource_user_id{rp.Pop<u64>()};
1251
1252 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1253 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1254
1255 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1256 npad_id_1, npad_id_2, applet_resource_user_id);
1257
1258 IPC::ResponseBuilder rb{ctx, 2};
1259 rb.Push(result);
1260}
1261
1262void Hid::StartLrAssignmentMode(HLERequestContext& ctx) {
1263 IPC::RequestParser rp{ctx};
1264 const auto applet_resource_user_id{rp.Pop<u64>()};
1265
1266 applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
1267
1268 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1269
1270 IPC::ResponseBuilder rb{ctx, 2};
1271 rb.Push(ResultSuccess);
1272}
1273
1274void Hid::StopLrAssignmentMode(HLERequestContext& ctx) {
1275 IPC::RequestParser rp{ctx};
1276 const auto applet_resource_user_id{rp.Pop<u64>()};
1277
1278 applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
1279
1280 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1281
1282 IPC::ResponseBuilder rb{ctx, 2};
1283 rb.Push(ResultSuccess);
1284}
1285
1286void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1287 IPC::RequestParser rp{ctx};
1288 const auto applet_resource_user_id{rp.Pop<u64>()};
1289 const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
1290
1291 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1292 .SetNpadHandheldActivationMode(activation_mode);
1293
1294 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1295 applet_resource_user_id, activation_mode);
1296
1297 IPC::ResponseBuilder rb{ctx, 2};
1298 rb.Push(ResultSuccess);
1299}
1300
1301void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1302 IPC::RequestParser rp{ctx};
1303 const auto applet_resource_user_id{rp.Pop<u64>()};
1304
1305 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1306
1307 IPC::ResponseBuilder rb{ctx, 4};
1308 rb.Push(ResultSuccess);
1309 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1310 .GetNpadHandheldActivationMode());
1311}
1312
1313void Hid::SwapNpadAssignment(HLERequestContext& ctx) {
1314 IPC::RequestParser rp{ctx};
1315 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1316 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1317 const auto applet_resource_user_id{rp.Pop<u64>()};
1318
1319 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1320 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
1321
1322 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1323 npad_id_1, npad_id_2, applet_resource_user_id);
1324
1325 IPC::ResponseBuilder rb{ctx, 2};
1326 rb.Push(result);
1327}
1328
1329void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1330 IPC::RequestParser rp{ctx};
1331 struct Parameters {
1332 Core::HID::NpadIdType npad_id;
1333 INSERT_PADDING_WORDS_NOINIT(1);
1334 u64 applet_resource_user_id;
1335 };
1336 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1337
1338 const auto parameters{rp.PopRaw<Parameters>()};
1339
1340 bool is_enabled = false;
1341 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1342 const auto result =
1343 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1344
1345 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1346 parameters.npad_id, parameters.applet_resource_user_id);
1347
1348 IPC::ResponseBuilder rb{ctx, 3};
1349 rb.Push(result);
1350 rb.Push(is_enabled);
1351}
1352
1353void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1354 IPC::RequestParser rp{ctx};
1355 struct Parameters {
1356 bool is_enabled;
1357 INSERT_PADDING_BYTES_NOINIT(3);
1358 Core::HID::NpadIdType npad_id;
1359 u64 applet_resource_user_id;
1360 };
1361 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1362
1363 const auto parameters{rp.PopRaw<Parameters>()};
1364
1365 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1366 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
1367 parameters.is_enabled, parameters.npad_id);
1368
1369 LOG_DEBUG(Service_HID,
1370 "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1371 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1372
1373 IPC::ResponseBuilder rb{ctx, 2};
1374 rb.Push(result);
1375}
1376
1377void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1378 IPC::RequestParser rp{ctx};
1379 struct Parameters {
1380 Core::HID::NpadIdType npad_id;
1381 INSERT_PADDING_WORDS_NOINIT(1);
1382 u64 applet_resource_user_id;
1383 Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
1384 };
1385 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1386
1387 const auto parameters{rp.PopRaw<Parameters>()};
1388
1389 Core::HID::NpadIdType new_npad_id{};
1390 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1391 const auto is_reassigned =
1392 controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1393 Controller_NPad::NpadJoyAssignmentMode::Single);
1394
1395 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1396 parameters.npad_id, parameters.applet_resource_user_id,
1397 parameters.npad_joy_device_type);
1398
1399 IPC::ResponseBuilder rb{ctx, 4};
1400 rb.Push(ResultSuccess);
1401 rb.Push(is_reassigned);
1402 rb.PushEnum(new_npad_id);
1403}
1404
1405void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1406 IPC::RequestParser rp{ctx};
1407 struct Parameters {
1408 bool analog_stick_use_center_clamp;
1409 INSERT_PADDING_BYTES_NOINIT(7);
1410 u64 applet_resource_user_id;
1411 };
1412 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1413
1414 const auto parameters{rp.PopRaw<Parameters>()};
1415
1416 GetAppletResource()
1417 ->GetController<Controller_NPad>(HidController::NPad)
1418 .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp);
1419
1420 LOG_WARNING(Service_HID,
1421 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1422 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1423
1424 IPC::ResponseBuilder rb{ctx, 2};
1425 rb.Push(ResultSuccess);
1426}
1427
1428void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1429 IPC::RequestParser rp{ctx};
1430 struct Parameters {
1431 Core::HID::NpadStyleSet npad_styleset;
1432 INSERT_PADDING_WORDS_NOINIT(1);
1433 u64 applet_resource_user_id;
1434 Core::HID::NpadButton button;
1435 };
1436 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1437
1438 const auto parameters{rp.PopRaw<Parameters>()};
1439
1440 LOG_WARNING(Service_HID,
1441 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1442 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1443
1444 IPC::ResponseBuilder rb{ctx, 2};
1445 rb.Push(ResultSuccess);
1446}
1447
1448void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1449 IPC::RequestParser rp{ctx};
1450 const auto applet_resource_user_id{rp.Pop<u64>()};
1451
1452 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1453 applet_resource_user_id);
1454
1455 IPC::ResponseBuilder rb{ctx, 2};
1456 rb.Push(ResultSuccess);
1457}
1458
1459void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1460 IPC::RequestParser rp{ctx};
1461 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1462 const auto& controller =
1463 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1464
1465 Core::HID::VibrationDeviceInfo vibration_device_info;
1466 bool check_device_index = false;
1467
1468 switch (vibration_device_handle.npad_type) {
1469 case Core::HID::NpadStyleIndex::ProController:
1470 case Core::HID::NpadStyleIndex::Handheld:
1471 case Core::HID::NpadStyleIndex::JoyconDual:
1472 case Core::HID::NpadStyleIndex::JoyconLeft:
1473 case Core::HID::NpadStyleIndex::JoyconRight:
1474 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1475 check_device_index = true;
1476 break;
1477 case Core::HID::NpadStyleIndex::GameCube:
1478 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1479 break;
1480 case Core::HID::NpadStyleIndex::N64:
1481 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1482 break;
1483 default:
1484 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1485 break;
1486 }
1487
1488 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1489 if (check_device_index) {
1490 switch (vibration_device_handle.device_index) {
1491 case Core::HID::DeviceIndex::Left:
1492 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1493 break;
1494 case Core::HID::DeviceIndex::Right:
1495 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1496 break;
1497 case Core::HID::DeviceIndex::None:
1498 default:
1499 ASSERT_MSG(false, "DeviceIndex should never be None!");
1500 break;
1501 }
1502 }
1503
1504 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1505 vibration_device_info.type, vibration_device_info.position);
1506
1507 const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
1508 if (result.IsError()) {
1509 IPC::ResponseBuilder rb{ctx, 2};
1510 rb.Push(result);
1511 return;
1512 }
1513
1514 IPC::ResponseBuilder rb{ctx, 4};
1515 rb.Push(ResultSuccess);
1516 rb.PushRaw(vibration_device_info);
1517}
1518
1519void Hid::SendVibrationValue(HLERequestContext& ctx) {
1520 IPC::RequestParser rp{ctx};
1521 struct Parameters {
1522 Core::HID::VibrationDeviceHandle vibration_device_handle;
1523 Core::HID::VibrationValue vibration_value;
1524 INSERT_PADDING_WORDS_NOINIT(1);
1525 u64 applet_resource_user_id;
1526 };
1527 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1528
1529 const auto parameters{rp.PopRaw<Parameters>()};
1530
1531 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1532 .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
1533
1534 LOG_DEBUG(Service_HID,
1535 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1536 parameters.vibration_device_handle.npad_type,
1537 parameters.vibration_device_handle.npad_id,
1538 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1539
1540 IPC::ResponseBuilder rb{ctx, 2};
1541 rb.Push(ResultSuccess);
1542}
1543
1544void Hid::GetActualVibrationValue(HLERequestContext& ctx) {
1545 IPC::RequestParser rp{ctx};
1546 struct Parameters {
1547 Core::HID::VibrationDeviceHandle vibration_device_handle;
1548 INSERT_PADDING_WORDS_NOINIT(1);
1549 u64 applet_resource_user_id;
1550 };
1551 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1552
1553 const auto parameters{rp.PopRaw<Parameters>()};
1554
1555 LOG_DEBUG(Service_HID,
1556 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1557 parameters.vibration_device_handle.npad_type,
1558 parameters.vibration_device_handle.npad_id,
1559 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1560
1561 IPC::ResponseBuilder rb{ctx, 6};
1562 rb.Push(ResultSuccess);
1563 rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1564 .GetLastVibration(parameters.vibration_device_handle));
1565}
1566
1567void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1568 LOG_DEBUG(Service_HID, "called");
1569
1570 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1571 rb.Push(ResultSuccess);
1572 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, applet_resource);
1573}
1574
1575void Hid::PermitVibration(HLERequestContext& ctx) {
1576 IPC::RequestParser rp{ctx};
1577 const auto can_vibrate{rp.Pop<bool>()};
1578
1579 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1580 // by converting it to a bool
1581 Settings::values.vibration_enabled.SetValue(can_vibrate);
1582
1583 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1584
1585 IPC::ResponseBuilder rb{ctx, 2};
1586 rb.Push(ResultSuccess);
1587}
1588
1589void Hid::IsVibrationPermitted(HLERequestContext& ctx) {
1590 LOG_DEBUG(Service_HID, "called");
1591
1592 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1593 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1594
1595 IPC::ResponseBuilder rb{ctx, 3};
1596 rb.Push(ResultSuccess);
1597 rb.Push(is_enabled);
1598}
1599
1600void Hid::SendVibrationValues(HLERequestContext& ctx) {
1601 IPC::RequestParser rp{ctx};
1602 const auto applet_resource_user_id{rp.Pop<u64>()};
1603
1604 const auto handle_data = ctx.ReadBuffer(0);
1605 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1606 const auto vibration_data = ctx.ReadBuffer(1);
1607 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1608
1609 auto vibration_device_handles =
1610 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1611 handle_count);
1612 auto vibration_values = std::span(
1613 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1614
1615 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1616 .VibrateControllers(vibration_device_handles, vibration_values);
1617
1618 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1619
1620 IPC::ResponseBuilder rb{ctx, 2};
1621 rb.Push(ResultSuccess);
1622}
1623
1624void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1625 IPC::RequestParser rp{ctx};
1626 struct Parameters {
1627 Core::HID::VibrationDeviceHandle vibration_device_handle;
1628 INSERT_PADDING_WORDS_NOINIT(1);
1629 u64 applet_resource_user_id;
1630 Core::HID::VibrationGcErmCommand gc_erm_command;
1631 };
1632 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1633
1634 const auto parameters{rp.PopRaw<Parameters>()};
1635
1636 /**
1637 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1638 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1639 * in order to differentiate between Stop and StopHard commands.
1640 * This is done to reuse the controller vibration functions made for regular controllers.
1641 */
1642 const auto vibration_value = [parameters] {
1643 switch (parameters.gc_erm_command) {
1644 case Core::HID::VibrationGcErmCommand::Stop:
1645 return Core::HID::VibrationValue{
1646 .low_amplitude = 0.0f,
1647 .low_frequency = 160.0f,
1648 .high_amplitude = 0.0f,
1649 .high_frequency = 320.0f,
1650 };
1651 case Core::HID::VibrationGcErmCommand::Start:
1652 return Core::HID::VibrationValue{
1653 .low_amplitude = 1.0f,
1654 .low_frequency = 160.0f,
1655 .high_amplitude = 1.0f,
1656 .high_frequency = 320.0f,
1657 };
1658 case Core::HID::VibrationGcErmCommand::StopHard:
1659 return Core::HID::VibrationValue{
1660 .low_amplitude = 0.0f,
1661 .low_frequency = 0.0f,
1662 .high_amplitude = 0.0f,
1663 .high_frequency = 0.0f,
1664 };
1665 default:
1666 return Core::HID::DEFAULT_VIBRATION_VALUE;
1667 }
1668 }();
1669
1670 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1671 .VibrateController(parameters.vibration_device_handle, vibration_value);
1672
1673 LOG_DEBUG(Service_HID,
1674 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1675 "gc_erm_command={}",
1676 parameters.vibration_device_handle.npad_type,
1677 parameters.vibration_device_handle.npad_id,
1678 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1679 parameters.gc_erm_command);
1680
1681 IPC::ResponseBuilder rb{ctx, 2};
1682 rb.Push(ResultSuccess);
1683}
1684
1685void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1686 IPC::RequestParser rp{ctx};
1687 struct Parameters {
1688 Core::HID::VibrationDeviceHandle vibration_device_handle;
1689 INSERT_PADDING_WORDS_NOINIT(1);
1690 u64 applet_resource_user_id;
1691 };
1692
1693 const auto parameters{rp.PopRaw<Parameters>()};
1694
1695 const auto last_vibration = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1696 .GetLastVibration(parameters.vibration_device_handle);
1697
1698 const auto gc_erm_command = [last_vibration] {
1699 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1700 return Core::HID::VibrationGcErmCommand::Start;
1701 }
1702
1703 /**
1704 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1705 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1706 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1707 * This is done to reuse the controller vibration functions made for regular controllers.
1708 */
1709 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1710 return Core::HID::VibrationGcErmCommand::StopHard;
1711 }
1712
1713 return Core::HID::VibrationGcErmCommand::Stop;
1714 }();
1715
1716 LOG_DEBUG(Service_HID,
1717 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1718 parameters.vibration_device_handle.npad_type,
1719 parameters.vibration_device_handle.npad_id,
1720 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1721
1722 IPC::ResponseBuilder rb{ctx, 4};
1723 rb.Push(ResultSuccess);
1724 rb.PushEnum(gc_erm_command);
1725}
1726
1727void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) {
1728 IPC::RequestParser rp{ctx};
1729 const auto applet_resource_user_id{rp.Pop<u64>()};
1730
1731 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1732 .SetPermitVibrationSession(true);
1733
1734 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1735
1736 IPC::ResponseBuilder rb{ctx, 2};
1737 rb.Push(ResultSuccess);
1738}
1739
1740void Hid::EndPermitVibrationSession(HLERequestContext& ctx) {
1741 applet_resource->GetController<Controller_NPad>(HidController::NPad)
1742 .SetPermitVibrationSession(false);
1743
1744 LOG_DEBUG(Service_HID, "called");
1745
1746 IPC::ResponseBuilder rb{ctx, 2};
1747 rb.Push(ResultSuccess);
1748}
1749
1750void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1751 IPC::RequestParser rp{ctx};
1752 struct Parameters {
1753 Core::HID::VibrationDeviceHandle vibration_device_handle;
1754 INSERT_PADDING_WORDS_NOINIT(1);
1755 u64 applet_resource_user_id;
1756 };
1757 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1758
1759 const auto parameters{rp.PopRaw<Parameters>()};
1760
1761 LOG_DEBUG(Service_HID,
1762 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1763 parameters.vibration_device_handle.npad_type,
1764 parameters.vibration_device_handle.npad_id,
1765 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1766
1767 IPC::ResponseBuilder rb{ctx, 3};
1768 rb.Push(ResultSuccess);
1769 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
1770 .IsVibrationDeviceMounted(parameters.vibration_device_handle));
1771}
1772
1773void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1774 IPC::RequestParser rp{ctx};
1775 const auto applet_resource_user_id{rp.Pop<u64>()};
1776
1777 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1778
1779 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1780
1781 IPC::ResponseBuilder rb{ctx, 2};
1782 rb.Push(ResultSuccess);
1783}
1784
1785void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1786 IPC::RequestParser rp{ctx};
1787 struct Parameters {
1788 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1789 INSERT_PADDING_WORDS_NOINIT(1);
1790 u64 applet_resource_user_id;
1791 };
1792 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1793
1794 const auto parameters{rp.PopRaw<Parameters>()};
1795
1796 LOG_WARNING(Service_HID,
1797 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1798 parameters.console_sixaxis_handle.unknown_1,
1799 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1800
1801 IPC::ResponseBuilder rb{ctx, 2};
1802 rb.Push(ResultSuccess);
1803}
1804
1805void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1806 IPC::RequestParser rp{ctx};
1807 struct Parameters {
1808 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1809 INSERT_PADDING_WORDS_NOINIT(1);
1810 u64 applet_resource_user_id;
1811 };
1812 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1813
1814 const auto parameters{rp.PopRaw<Parameters>()};
1815
1816 LOG_WARNING(Service_HID,
1817 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1818 parameters.console_sixaxis_handle.unknown_1,
1819 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1820
1821 IPC::ResponseBuilder rb{ctx, 2};
1822 rb.Push(ResultSuccess);
1823}
1824
1825void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1826 IPC::RequestParser rp{ctx};
1827 const auto applet_resource_user_id{rp.Pop<u64>()};
1828
1829 applet_resource->ActivateController(HidController::ConsoleSixAxisSensor);
1830
1831 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1832
1833 IPC::ResponseBuilder rb{ctx, 2};
1834 rb.Push(ResultSuccess);
1835}
1836
1837void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1838 IPC::RequestParser rp{ctx};
1839 const auto applet_resource_user_id{rp.Pop<u64>()};
1840
1841 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1842 applet_resource_user_id);
1843
1844 IPC::ResponseBuilder rb{ctx, 2};
1845 rb.Push(ResultSuccess);
1846}
1847
1848void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1849 IPC::RequestParser rp{ctx};
1850 const auto applet_resource_user_id{rp.Pop<u64>()};
1851
1852 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1853 applet_resource_user_id);
1854
1855 IPC::ResponseBuilder rb{ctx, 2};
1856 rb.Push(ResultSuccess);
1857}
1858
1859void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1860 IPC::RequestParser rp{ctx};
1861 const auto applet_resource_user_id{rp.Pop<u64>()};
1862 const auto t_mem_1_size{rp.Pop<u64>()};
1863 const auto t_mem_2_size{rp.Pop<u64>()};
1864 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1865 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1866
1867 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1868 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1869
1870 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1871 t_mem_1_handle);
1872
1873 if (t_mem_1.IsNull()) {
1874 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1875 IPC::ResponseBuilder rb{ctx, 2};
1876 rb.Push(ResultUnknown);
1877 return;
1878 }
1879
1880 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1881 t_mem_2_handle);
1882
1883 if (t_mem_2.IsNull()) {
1884 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1885 IPC::ResponseBuilder rb{ctx, 2};
1886 rb.Push(ResultUnknown);
1887 return;
1888 }
1889
1890 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1891 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1892
1893 // Activate console six axis controller
1894 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1895 .ActivateController();
1896
1897 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1898 .SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1899
1900 LOG_WARNING(Service_HID,
1901 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1902 "applet_resource_user_id={}",
1903 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1904
1905 IPC::ResponseBuilder rb{ctx, 2};
1906 rb.Push(ResultSuccess);
1907}
1908
1909void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1910 IPC::RequestParser rp{ctx};
1911 const auto applet_resource_user_id{rp.Pop<u64>()};
1912
1913 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1914 applet_resource_user_id);
1915
1916 IPC::ResponseBuilder rb{ctx, 2};
1917 rb.Push(ResultSuccess);
1918}
1919
1920void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1921 IPC::RequestParser rp{ctx};
1922 const auto applet_resource_user_id{rp.Pop<u64>()};
1923
1924 applet_resource->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1925 .ResetTimestamp();
1926
1927 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1928
1929 IPC::ResponseBuilder rb{ctx, 2};
1930 rb.Push(ResultSuccess);
1931}
1932
1933void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1934 IPC::RequestParser rp{ctx};
1935
1936 LOG_WARNING(Service_HID, "(STUBBED) called");
1937
1938 IPC::ResponseBuilder rb{ctx, 3};
1939 rb.Push(ResultSuccess);
1940 rb.Push(false);
1941}
1942
1943void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1944 IPC::RequestParser rp{ctx};
1945 struct Parameters {
1946 Core::HID::NpadIdType npad_id;
1947 INSERT_PADDING_WORDS_NOINIT(1);
1948 u64 applet_resource_user_id;
1949 };
1950 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1951
1952 const auto parameters{rp.PopRaw<Parameters>()};
1953
1954 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1955 parameters.npad_id, parameters.applet_resource_user_id);
1956
1957 Controller_Palma::PalmaConnectionHandle handle;
1958 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1959 const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle);
1960
1961 IPC::ResponseBuilder rb{ctx, 4};
1962 rb.Push(result);
1963 rb.PushRaw(handle);
1964}
1965
1966void Hid::InitializePalma(HLERequestContext& ctx) {
1967 IPC::RequestParser rp{ctx};
1968 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1969
1970 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1971
1972 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1973 const auto result = controller.InitializePalma(connection_handle);
1974
1975 IPC::ResponseBuilder rb{ctx, 2};
1976 rb.Push(result);
1977}
1978
1979void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1980 IPC::RequestParser rp{ctx};
1981 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1982
1983 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1984
1985 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
1986
1987 IPC::ResponseBuilder rb{ctx, 2, 1};
1988 rb.Push(ResultSuccess);
1989 rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle));
1990}
1991
1992void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) {
1993 IPC::RequestParser rp{ctx};
1994 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
1995
1996 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1997
1998 Controller_Palma::PalmaOperationType operation_type;
1999 Controller_Palma::PalmaOperationData data;
2000 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2001 const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data);
2002
2003 if (result.IsError()) {
2004 IPC::ResponseBuilder rb{ctx, 2};
2005 rb.Push(result);
2006 }
2007
2008 ctx.WriteBuffer(data);
2009 IPC::ResponseBuilder rb{ctx, 4};
2010 rb.Push(result);
2011 rb.Push(static_cast<u64>(operation_type));
2012}
2013
2014void Hid::PlayPalmaActivity(HLERequestContext& ctx) {
2015 IPC::RequestParser rp{ctx};
2016 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2017 const auto palma_activity{rp.Pop<u64>()};
2018
2019 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
2020 connection_handle.npad_id, palma_activity);
2021
2022 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2023 const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity);
2024
2025 IPC::ResponseBuilder rb{ctx, 2};
2026 rb.Push(result);
2027}
2028
2029void Hid::SetPalmaFrModeType(HLERequestContext& ctx) {
2030 IPC::RequestParser rp{ctx};
2031 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2032 const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()};
2033
2034 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2035 connection_handle.npad_id, fr_mode);
2036
2037 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2038 const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode);
2039
2040 IPC::ResponseBuilder rb{ctx, 2};
2041 rb.Push(result);
2042}
2043
2044void Hid::ReadPalmaStep(HLERequestContext& ctx) {
2045 IPC::RequestParser rp{ctx};
2046 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2047
2048 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2049
2050 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2051 const auto result = controller.ReadPalmaStep(connection_handle);
2052
2053 IPC::ResponseBuilder rb{ctx, 2};
2054 rb.Push(result);
2055}
2056
2057void Hid::EnablePalmaStep(HLERequestContext& ctx) {
2058 IPC::RequestParser rp{ctx};
2059 struct Parameters {
2060 bool is_enabled;
2061 INSERT_PADDING_WORDS_NOINIT(1);
2062 Controller_Palma::PalmaConnectionHandle connection_handle;
2063 };
2064 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2065
2066 const auto parameters{rp.PopRaw<Parameters>()};
2067
2068 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2069 parameters.connection_handle.npad_id, parameters.is_enabled);
2070
2071 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2072 const auto result =
2073 controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2074
2075 IPC::ResponseBuilder rb{ctx, 2};
2076 rb.Push(result);
2077}
2078
2079void Hid::ResetPalmaStep(HLERequestContext& ctx) {
2080 IPC::RequestParser rp{ctx};
2081 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2082
2083 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2084
2085 auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
2086 const auto result = controller.ResetPalmaStep(connection_handle);
2087
2088 IPC::ResponseBuilder rb{ctx, 2};
2089 rb.Push(result);
2090}
2091
2092void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2093 LOG_WARNING(Service_HID, "(STUBBED) called");
2094
2095 IPC::ResponseBuilder rb{ctx, 2};
2096 rb.Push(ResultSuccess);
2097}
2098
2099void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) {
2100 LOG_WARNING(Service_HID, "(STUBBED) called");
2101
2102 IPC::ResponseBuilder rb{ctx, 2};
2103 rb.Push(ResultSuccess);
2104}
2105
2106void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2107 IPC::RequestParser rp{ctx};
2108 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2109
2110 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2111
2112 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2113 .ReadPalmaUniqueCode(connection_handle);
2114
2115 IPC::ResponseBuilder rb{ctx, 2};
2116 rb.Push(ResultSuccess);
2117}
2118
2119void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2120 IPC::RequestParser rp{ctx};
2121 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2122
2123 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2124
2125 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2126 .SetPalmaUniqueCodeInvalid(connection_handle);
2127
2128 IPC::ResponseBuilder rb{ctx, 2};
2129 rb.Push(ResultSuccess);
2130}
2131
2132void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) {
2133 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2134
2135 IPC::ResponseBuilder rb{ctx, 2};
2136 rb.Push(ResultSuccess);
2137}
2138
2139void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2140 IPC::RequestParser rp{ctx};
2141 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2142 const auto unknown{rp.Pop<u64>()};
2143
2144 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2145
2146 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2147 connection_handle.npad_id, unknown);
2148
2149 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2150 .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2151
2152 IPC::ResponseBuilder rb{ctx, 2};
2153 rb.Push(ResultSuccess);
2154}
2155
2156void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) {
2157 IPC::RequestParser rp{ctx};
2158 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2159 const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()};
2160 const auto unknown{rp.Pop<u64>()};
2161 const auto t_mem_size{rp.Pop<u64>()};
2162 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2163 const auto size{rp.Pop<u64>()};
2164
2165 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2166
2167 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2168 t_mem_handle);
2169
2170 if (t_mem.IsNull()) {
2171 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2172 IPC::ResponseBuilder rb{ctx, 2};
2173 rb.Push(ResultUnknown);
2174 return;
2175 }
2176
2177 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2178
2179 LOG_WARNING(Service_HID,
2180 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2181 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2182 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2183
2184 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2185 .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size);
2186
2187 IPC::ResponseBuilder rb{ctx, 2};
2188 rb.Push(ResultSuccess);
2189}
2190
2191void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2192 IPC::RequestParser rp{ctx};
2193 struct Parameters {
2194 s32 database_id_version;
2195 INSERT_PADDING_WORDS_NOINIT(1);
2196 Controller_Palma::PalmaConnectionHandle connection_handle;
2197 };
2198 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2199
2200 const auto parameters{rp.PopRaw<Parameters>()};
2201
2202 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2203 parameters.connection_handle.npad_id, parameters.database_id_version);
2204
2205 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2206 .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
2207 parameters.database_id_version);
2208
2209 IPC::ResponseBuilder rb{ctx, 2};
2210 rb.Push(ResultSuccess);
2211}
2212
2213void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2214 IPC::RequestParser rp{ctx};
2215 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2216
2217 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2218
2219 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2220 .GetPalmaDataBaseIdentificationVersion(connection_handle);
2221
2222 IPC::ResponseBuilder rb{ctx, 2};
2223 rb.Push(ResultSuccess);
2224}
2225
2226void Hid::SuspendPalmaFeature(HLERequestContext& ctx) {
2227 LOG_WARNING(Service_HID, "(STUBBED) called");
2228
2229 IPC::ResponseBuilder rb{ctx, 2};
2230 rb.Push(ResultSuccess);
2231}
2232
2233void Hid::GetPalmaOperationResult(HLERequestContext& ctx) {
2234 IPC::RequestParser rp{ctx};
2235 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2236
2237 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2238
2239 const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma)
2240 .GetPalmaOperationResult(connection_handle);
2241
2242 IPC::ResponseBuilder rb{ctx, 2};
2243 rb.Push(result);
2244}
2245
2246void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) {
2247 LOG_WARNING(Service_HID, "(STUBBED) called");
2248
2249 IPC::ResponseBuilder rb{ctx, 2};
2250 rb.Push(ResultSuccess);
2251}
2252
2253void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) {
2254 LOG_WARNING(Service_HID, "(STUBBED) called");
2255
2256 IPC::ResponseBuilder rb{ctx, 2};
2257 rb.Push(ResultSuccess);
2258}
2259
2260void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2261 IPC::RequestParser rp{ctx};
2262 struct Parameters {
2263 bool is_palma_all_connectable;
2264 INSERT_PADDING_BYTES_NOINIT(7);
2265 u64 applet_resource_user_id;
2266 };
2267 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2268
2269 const auto parameters{rp.PopRaw<Parameters>()};
2270
2271 LOG_WARNING(Service_HID,
2272 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2273 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2274
2275 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2276 .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2277
2278 IPC::ResponseBuilder rb{ctx, 2};
2279 rb.Push(ResultSuccess);
2280}
2281
2282void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2283 LOG_WARNING(Service_HID, "(STUBBED) called");
2284
2285 IPC::ResponseBuilder rb{ctx, 2};
2286 rb.Push(ResultSuccess);
2287}
2288
2289void Hid::PairPalma(HLERequestContext& ctx) {
2290 IPC::RequestParser rp{ctx};
2291 const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
2292
2293 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2294
2295 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2296 .PairPalma(connection_handle);
2297
2298 IPC::ResponseBuilder rb{ctx, 2};
2299 rb.Push(ResultSuccess);
2300}
2301
2302void Hid::SetPalmaBoostMode(HLERequestContext& ctx) {
2303 IPC::RequestParser rp{ctx};
2304 const auto palma_boost_mode{rp.Pop<bool>()};
2305
2306 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2307
2308 applet_resource->GetController<Controller_Palma>(HidController::Palma)
2309 .SetPalmaBoostMode(palma_boost_mode);
2310
2311 IPC::ResponseBuilder rb{ctx, 2};
2312 rb.Push(ResultSuccess);
2313}
2314
2315void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2316 LOG_WARNING(Service_HID, "(STUBBED) called");
2317
2318 IPC::ResponseBuilder rb{ctx, 2};
2319 rb.Push(ResultSuccess);
2320}
2321
2322void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) {
2323 LOG_WARNING(Service_HID, "(STUBBED) called");
2324
2325 IPC::ResponseBuilder rb{ctx, 2};
2326 rb.Push(ResultSuccess);
2327}
2328
2329void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2330 LOG_WARNING(Service_HID, "(STUBBED) called");
2331
2332 IPC::ResponseBuilder rb{ctx, 2};
2333 rb.Push(ResultSuccess);
2334}
2335
2336void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2337 LOG_WARNING(Service_HID, "(STUBBED) called");
2338
2339 IPC::ResponseBuilder rb{ctx, 2};
2340 rb.Push(ResultSuccess);
2341}
2342
2343void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) {
2344 IPC::RequestParser rp{ctx};
2345 const auto applet_resource_user_id{rp.Pop<u64>()};
2346 const auto communication_mode{rp.PopEnum<Controller_NPad::NpadCommunicationMode>()};
2347
2348 applet_resource->GetController<Controller_NPad>(HidController::NPad)
2349 .SetNpadCommunicationMode(communication_mode);
2350
2351 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2352 applet_resource_user_id, communication_mode);
2353
2354 IPC::ResponseBuilder rb{ctx, 2};
2355 rb.Push(ResultSuccess);
2356}
2357
2358void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) {
2359 IPC::RequestParser rp{ctx};
2360
2361 LOG_WARNING(Service_HID, "(STUBBED) called");
2362
2363 IPC::ResponseBuilder rb{ctx, 4};
2364 rb.Push(ResultSuccess);
2365 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
2366 .GetNpadCommunicationMode());
2367}
2368
2369void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2370 IPC::RequestParser rp{ctx};
2371 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2372 const auto applet_resource_user_id{rp.Pop<u64>()};
2373
2374 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2375 touchscreen_mode.mode, applet_resource_user_id);
2376
2377 IPC::ResponseBuilder rb{ctx, 2};
2378 rb.Push(ResultSuccess);
2379}
2380
2381void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2382 IPC::RequestParser rp{ctx};
2383 struct Parameters {
2384 s32 unknown;
2385 INSERT_PADDING_WORDS_NOINIT(1);
2386 u64 applet_resource_user_id;
2387 };
2388 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2389
2390 const auto parameters{rp.PopRaw<Parameters>()};
2391
2392 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2393 parameters.unknown, parameters.applet_resource_user_id);
2394
2395 IPC::ResponseBuilder rb{ctx, 3};
2396 rb.Push(ResultSuccess);
2397 rb.Push(false);
2398}
2399
2400class HidDbg final : public ServiceFramework<HidDbg> {
2401public:
2402 explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
2403 // clang-format off
2404 static const FunctionInfo functions[] = {
2405 {0, nullptr, "DeactivateDebugPad"},
2406 {1, nullptr, "SetDebugPadAutoPilotState"},
2407 {2, nullptr, "UnsetDebugPadAutoPilotState"},
2408 {10, nullptr, "DeactivateTouchScreen"},
2409 {11, nullptr, "SetTouchScreenAutoPilotState"},
2410 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
2411 {13, nullptr, "GetTouchScreenConfiguration"},
2412 {14, nullptr, "ProcessTouchScreenAutoTune"},
2413 {15, nullptr, "ForceStopTouchScreenManagement"},
2414 {16, nullptr, "ForceRestartTouchScreenManagement"},
2415 {17, nullptr, "IsTouchScreenManaged"},
2416 {20, nullptr, "DeactivateMouse"},
2417 {21, nullptr, "SetMouseAutoPilotState"},
2418 {22, nullptr, "UnsetMouseAutoPilotState"},
2419 {25, nullptr, "SetDebugMouseAutoPilotState"},
2420 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
2421 {30, nullptr, "DeactivateKeyboard"},
2422 {31, nullptr, "SetKeyboardAutoPilotState"},
2423 {32, nullptr, "UnsetKeyboardAutoPilotState"},
2424 {50, nullptr, "DeactivateXpad"},
2425 {51, nullptr, "SetXpadAutoPilotState"},
2426 {52, nullptr, "UnsetXpadAutoPilotState"},
2427 {53, nullptr, "DeactivateJoyXpad"},
2428 {60, nullptr, "ClearNpadSystemCommonPolicy"},
2429 {61, nullptr, "DeactivateNpad"},
2430 {62, nullptr, "ForceDisconnectNpad"},
2431 {91, nullptr, "DeactivateGesture"},
2432 {110, nullptr, "DeactivateHomeButton"},
2433 {111, nullptr, "SetHomeButtonAutoPilotState"},
2434 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
2435 {120, nullptr, "DeactivateSleepButton"},
2436 {121, nullptr, "SetSleepButtonAutoPilotState"},
2437 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
2438 {123, nullptr, "DeactivateInputDetector"},
2439 {130, nullptr, "DeactivateCaptureButton"},
2440 {131, nullptr, "SetCaptureButtonAutoPilotState"},
2441 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
2442 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
2443 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
2444 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
2445 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
2446 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
2447 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
2448 {142, nullptr, "DeactivateSevenSixAxisSensor"},
2449 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
2450 {144, nullptr, "GetAccelerometerFsr"},
2451 {145, nullptr, "SetAccelerometerFsr"},
2452 {146, nullptr, "GetAccelerometerOdr"},
2453 {147, nullptr, "SetAccelerometerOdr"},
2454 {148, nullptr, "GetGyroscopeFsr"},
2455 {149, nullptr, "SetGyroscopeFsr"},
2456 {150, nullptr, "GetGyroscopeOdr"},
2457 {151, nullptr, "SetGyroscopeOdr"},
2458 {152, nullptr, "GetWhoAmI"},
2459 {201, nullptr, "ActivateFirmwareUpdate"},
2460 {202, nullptr, "DeactivateFirmwareUpdate"},
2461 {203, nullptr, "StartFirmwareUpdate"},
2462 {204, nullptr, "GetFirmwareUpdateStage"},
2463 {205, nullptr, "GetFirmwareVersion"},
2464 {206, nullptr, "GetDestinationFirmwareVersion"},
2465 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
2466 {208, nullptr, "StartFirmwareUpdateForRevert"},
2467 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
2468 {210, nullptr, "IsFirmwareUpdatingDevice"},
2469 {211, nullptr, "StartFirmwareUpdateIndividual"},
2470 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
2471 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
2472 {221, nullptr, "UpdateControllerColor"},
2473 {222, nullptr, "ConnectUsbPadsAsync"},
2474 {223, nullptr, "DisconnectUsbPadsAsync"},
2475 {224, nullptr, "UpdateDesignInfo"},
2476 {225, nullptr, "GetUniquePadDriverState"},
2477 {226, nullptr, "GetSixAxisSensorDriverStates"},
2478 {227, nullptr, "GetRxPacketHistory"},
2479 {228, nullptr, "AcquireOperationEventHandle"},
2480 {229, nullptr, "ReadSerialFlash"},
2481 {230, nullptr, "WriteSerialFlash"},
2482 {231, nullptr, "GetOperationResult"},
2483 {232, nullptr, "EnableShipmentMode"},
2484 {233, nullptr, "ClearPairingInfo"},
2485 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
2486 {235, nullptr, "EnableAnalogStickPower"},
2487 {236, nullptr, "RequestKuinaUartClockCal"},
2488 {237, nullptr, "GetKuinaUartClockCal"},
2489 {238, nullptr, "SetKuinaUartClockTrim"},
2490 {239, nullptr, "KuinaLoopbackTest"},
2491 {240, nullptr, "RequestBatteryVoltage"},
2492 {241, nullptr, "GetBatteryVoltage"},
2493 {242, nullptr, "GetUniquePadPowerInfo"},
2494 {243, nullptr, "RebootUniquePad"},
2495 {244, nullptr, "RequestKuinaFirmwareVersion"},
2496 {245, nullptr, "GetKuinaFirmwareVersion"},
2497 {246, nullptr, "GetVidPid"},
2498 {247, nullptr, "GetAnalogStickCalibrationValue"},
2499 {248, nullptr, "GetUniquePadIdsFull"},
2500 {249, nullptr, "ConnectUniquePad"},
2501 {250, nullptr, "IsVirtual"},
2502 {251, nullptr, "GetAnalogStickModuleParam"},
2503 {301, nullptr, "GetAbstractedPadHandles"},
2504 {302, nullptr, "GetAbstractedPadState"},
2505 {303, nullptr, "GetAbstractedPadsState"},
2506 {321, nullptr, "SetAutoPilotVirtualPadState"},
2507 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
2508 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
2509 {324, nullptr, "AttachHdlsWorkBuffer"},
2510 {325, nullptr, "ReleaseHdlsWorkBuffer"},
2511 {326, nullptr, "DumpHdlsNpadAssignmentState"},
2512 {327, nullptr, "DumpHdlsStates"},
2513 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
2514 {329, nullptr, "ApplyHdlsStateList"},
2515 {330, nullptr, "AttachHdlsVirtualDevice"},
2516 {331, nullptr, "DetachHdlsVirtualDevice"},
2517 {332, nullptr, "SetHdlsState"},
2518 {350, nullptr, "AddRegisteredDevice"},
2519 {400, nullptr, "DisableExternalMcuOnNxDevice"},
2520 {401, nullptr, "DisableRailDeviceFiltering"},
2521 {402, nullptr, "EnableWiredPairing"},
2522 {403, nullptr, "EnableShipmentModeAutoClear"},
2523 {404, nullptr, "SetRailEnabled"},
2524 {500, nullptr, "SetFactoryInt"},
2525 {501, nullptr, "IsFactoryBootEnabled"},
2526 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
2527 {551, nullptr, "GetAnalogStickModelData"},
2528 {552, nullptr, "ResetAnalogStickModelData"},
2529 {600, nullptr, "ConvertPadState"},
2530 {650, nullptr, "AddButtonPlayData"},
2531 {651, nullptr, "StartButtonPlayData"},
2532 {652, nullptr, "StopButtonPlayData"},
2533 {2000, nullptr, "DeactivateDigitizer"},
2534 {2001, nullptr, "SetDigitizerAutoPilotState"},
2535 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
2536 {2002, nullptr, "ReloadFirmwareDebugSettings"},
2537 };
2538 // clang-format on
2539
2540 RegisterHandlers(functions);
2541 }
2542};
2543
2544class HidSys final : public ServiceFramework<HidSys> {
2545public:
2546 explicit HidSys(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_)
2547 : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"},
2548 applet_resource{applet_resource_} {
2549 // clang-format off
2550 static const FunctionInfo functions[] = {
2551 {31, nullptr, "SendKeyboardLockKeyEvent"},
2552 {101, nullptr, "AcquireHomeButtonEventHandle"},
2553 {111, nullptr, "ActivateHomeButton"},
2554 {121, nullptr, "AcquireSleepButtonEventHandle"},
2555 {131, nullptr, "ActivateSleepButton"},
2556 {141, nullptr, "AcquireCaptureButtonEventHandle"},
2557 {151, nullptr, "ActivateCaptureButton"},
2558 {161, nullptr, "GetPlatformConfig"},
2559 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
2560 {211, nullptr, "GetNpadsWithNfc"},
2561 {212, nullptr, "AcquireNfcActivateEventHandle"},
2562 {213, nullptr, "ActivateNfc"},
2563 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
2564 {215, nullptr, "IsNfcActivated"},
2565 {230, nullptr, "AcquireIrSensorEventHandle"},
2566 {231, nullptr, "ActivateIrSensor"},
2567 {232, nullptr, "GetIrSensorState"},
2568 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2569 {301, nullptr, "ActivateNpadSystem"},
2570 {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
2571 {304, nullptr, "EnableAssigningSingleOnSlSrPress"},
2572 {305, nullptr, "DisableAssigningSingleOnSlSrPress"},
2573 {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"},
2574 {307, nullptr, "GetNpadSystemExtStyle"},
2575 {308, nullptr, "ApplyNpadSystemCommonPolicyFull"},
2576 {309, nullptr, "GetNpadFullKeyGripColor"},
2577 {310, nullptr, "GetMaskedSupportedNpadStyleSet"},
2578 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
2579 {312, nullptr, "SetSupportedNpadStyleSetAll"},
2580 {313, nullptr, "GetNpadCaptureButtonAssignment"},
2581 {314, nullptr, "GetAppletFooterUiType"},
2582 {315, nullptr, "GetAppletDetailedUiType"},
2583 {316, nullptr, "GetNpadInterfaceType"},
2584 {317, nullptr, "GetNpadLeftRightInterfaceType"},
2585 {318, nullptr, "HasBattery"},
2586 {319, nullptr, "HasLeftRightBattery"},
2587 {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
2588 {322, nullptr, "GetIrSensorState"},
2589 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
2590 {324, nullptr, "GetUniquePadButtonSet"},
2591 {325, nullptr, "GetUniquePadColor"},
2592 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
2593 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
2594 {328, nullptr, "AttachAbstractedPadToNpad"},
2595 {329, nullptr, "DetachAbstractedPadAll"},
2596 {330, nullptr, "CheckAbstractedPadConnection"},
2597 {500, nullptr, "SetAppletResourceUserId"},
2598 {501, nullptr, "RegisterAppletResourceUserId"},
2599 {502, nullptr, "UnregisterAppletResourceUserId"},
2600 {503, nullptr, "EnableAppletToGetInput"},
2601 {504, nullptr, "SetAruidValidForVibration"},
2602 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
2603 {506, nullptr, "EnableAppletToGetPadInput"},
2604 {507, nullptr, "EnableAppletToGetTouchScreen"},
2605 {510, nullptr, "SetVibrationMasterVolume"},
2606 {511, nullptr, "GetVibrationMasterVolume"},
2607 {512, nullptr, "BeginPermitVibrationSession"},
2608 {513, nullptr, "EndPermitVibrationSession"},
2609 {514, nullptr, "Unknown514"},
2610 {520, nullptr, "EnableHandheldHids"},
2611 {521, nullptr, "DisableHandheldHids"},
2612 {522, nullptr, "SetJoyConRailEnabled"},
2613 {523, nullptr, "IsJoyConRailEnabled"},
2614 {524, nullptr, "IsHandheldHidsEnabled"},
2615 {525, nullptr, "IsJoyConAttachedOnAllRail"},
2616 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
2617 {541, nullptr, "GetPlayReportControllerUsages"},
2618 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
2619 {543, nullptr, "GetRegisteredDevicesOld"},
2620 {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"},
2621 {545, nullptr, "SendConnectionTrigger"},
2622 {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"},
2623 {547, nullptr, "GetAllowedBluetoothLinksCount"},
2624 {548, nullptr, "GetRegisteredDevices"},
2625 {549, nullptr, "GetConnectableRegisteredDevices"},
2626 {700, nullptr, "ActivateUniquePad"},
2627 {702, nullptr, "AcquireUniquePadConnectionEventHandle"},
2628 {703, nullptr, "GetUniquePadIds"},
2629 {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
2630 {800, nullptr, "ListSixAxisSensorHandles"},
2631 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
2632 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
2633 {803, nullptr, "StartSixAxisSensorUserCalibration"},
2634 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
2635 {805, nullptr, "GetUniquePadBluetoothAddress"},
2636 {806, nullptr, "DisconnectUniquePad"},
2637 {807, nullptr, "GetUniquePadType"},
2638 {808, nullptr, "GetUniquePadInterface"},
2639 {809, nullptr, "GetUniquePadSerialNumber"},
2640 {810, nullptr, "GetUniquePadControllerNumber"},
2641 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
2642 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
2643 {821, nullptr, "StartAnalogStickManualCalibration"},
2644 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
2645 {823, nullptr, "CancelAnalogStickManualCalibration"},
2646 {824, nullptr, "ResetAnalogStickManualCalibration"},
2647 {825, nullptr, "GetAnalogStickState"},
2648 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
2649 {827, nullptr, "IsAnalogStickButtonPressed"},
2650 {828, nullptr, "IsAnalogStickInReleasePosition"},
2651 {829, nullptr, "IsAnalogStickInCircumference"},
2652 {830, nullptr, "SetNotificationLedPattern"},
2653 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
2654 {832, nullptr, "PrepareHidsForNotificationWake"},
2655 {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
2656 {851, nullptr, "EnableUsbFullKeyController"},
2657 {852, nullptr, "IsUsbConnected"},
2658 {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"},
2659 {900, nullptr, "ActivateInputDetector"},
2660 {901, nullptr, "NotifyInputDetector"},
2661 {1000, nullptr, "InitializeFirmwareUpdate"},
2662 {1001, nullptr, "GetFirmwareVersion"},
2663 {1002, nullptr, "GetAvailableFirmwareVersion"},
2664 {1003, nullptr, "IsFirmwareUpdateAvailable"},
2665 {1004, nullptr, "CheckFirmwareUpdateRequired"},
2666 {1005, nullptr, "StartFirmwareUpdate"},
2667 {1006, nullptr, "AbortFirmwareUpdate"},
2668 {1007, nullptr, "GetFirmwareUpdateState"},
2669 {1008, nullptr, "ActivateAudioControl"},
2670 {1009, nullptr, "AcquireAudioControlEventHandle"},
2671 {1010, nullptr, "GetAudioControlStates"},
2672 {1011, nullptr, "DeactivateAudioControl"},
2673 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
2674 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
2675 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
2676 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
2677 {1100, nullptr, "GetHidbusSystemServiceObject"},
2678 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
2679 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
2680 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
2681 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
2682 {1133, nullptr, "StartUsbFirmwareUpdate"},
2683 {1134, nullptr, "GetUsbFirmwareUpdateState"},
2684 {1150, nullptr, "SetTouchScreenMagnification"},
2685 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
2686 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
2687 {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
2688 {1154, nullptr, "IsFirmwareAvailableForNotification"},
2689 {1155, nullptr, "SetForceHandheldStyleVibration"},
2690 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
2691 {1157, nullptr, "CancelConnectionTrigger"},
2692 {1200, nullptr, "IsButtonConfigSupported"},
2693 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
2694 {1202, nullptr, "DeleteButtonConfig"},
2695 {1203, nullptr, "DeleteButtonConfigEmbedded"},
2696 {1204, nullptr, "SetButtonConfigEnabled"},
2697 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
2698 {1206, nullptr, "IsButtonConfigEnabled"},
2699 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
2700 {1208, nullptr, "SetButtonConfigEmbedded"},
2701 {1209, nullptr, "SetButtonConfigFull"},
2702 {1210, nullptr, "SetButtonConfigLeft"},
2703 {1211, nullptr, "SetButtonConfigRight"},
2704 {1212, nullptr, "GetButtonConfigEmbedded"},
2705 {1213, nullptr, "GetButtonConfigFull"},
2706 {1214, nullptr, "GetButtonConfigLeft"},
2707 {1215, nullptr, "GetButtonConfigRight"},
2708 {1250, nullptr, "IsCustomButtonConfigSupported"},
2709 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
2710 {1252, nullptr, "IsDefaultButtonConfigFull"},
2711 {1253, nullptr, "IsDefaultButtonConfigLeft"},
2712 {1254, nullptr, "IsDefaultButtonConfigRight"},
2713 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
2714 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
2715 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
2716 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
2717 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
2718 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
2719 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
2720 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
2721 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
2722 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
2723 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
2724 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
2725 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
2726 {1268, nullptr, "DeleteButtonConfigStorageFull"},
2727 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
2728 {1270, nullptr, "DeleteButtonConfigStorageRight"},
2729 {1271, nullptr, "IsUsingCustomButtonConfig"},
2730 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
2731 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
2732 {1274, nullptr, "SetDefaultButtonConfig"},
2733 {1275, nullptr, "SetAllDefaultButtonConfig"},
2734 {1276, nullptr, "SetHidButtonConfigEmbedded"},
2735 {1277, nullptr, "SetHidButtonConfigFull"},
2736 {1278, nullptr, "SetHidButtonConfigLeft"},
2737 {1279, nullptr, "SetHidButtonConfigRight"},
2738 {1280, nullptr, "GetHidButtonConfigEmbedded"},
2739 {1281, nullptr, "GetHidButtonConfigFull"},
2740 {1282, nullptr, "GetHidButtonConfigLeft"},
2741 {1283, nullptr, "GetHidButtonConfigRight"},
2742 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
2743 {1285, nullptr, "GetButtonConfigStorageFull"},
2744 {1286, nullptr, "GetButtonConfigStorageLeft"},
2745 {1287, nullptr, "GetButtonConfigStorageRight"},
2746 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
2747 {1289, nullptr, "SetButtonConfigStorageFull"},
2748 {1290, nullptr, "DeleteButtonConfigStorageRight"},
2749 {1291, nullptr, "DeleteButtonConfigStorageRight"},
2750 };
2751 // clang-format on
2752
2753 RegisterHandlers(functions);
2754
2755 joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent");
2756 }
2757
2758 ~HidSys() {
2759 service_context.CloseEvent(joy_detach_event);
2760 };
2761
2762private:
2763 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
2764 LOG_WARNING(Service_HID, "called");
2765
2766 GetAppletResource()
2767 ->GetController<Controller_NPad>(HidController::NPad)
2768 .ApplyNpadSystemCommonPolicy();
2769
2770 IPC::ResponseBuilder rb{ctx, 2};
2771 rb.Push(ResultSuccess);
2772 }
2773
2774 void GetLastActiveNpad(HLERequestContext& ctx) {
2775 LOG_DEBUG(Service_HID, "(STUBBED) called");
2776
2777 IPC::ResponseBuilder rb{ctx, 3};
2778 rb.Push(ResultSuccess);
2779 rb.PushEnum(system.HIDCore().GetLastActiveController());
2780 }
2781
2782 void GetUniquePadsFromNpad(HLERequestContext& ctx) {
2783 IPC::RequestParser rp{ctx};
2784 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
2785
2786 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type);
2787
2788 const std::vector<Core::HID::UniquePadId> unique_pads{};
2789
2790 ctx.WriteBuffer(unique_pads);
2791
2792 IPC::ResponseBuilder rb{ctx, 3};
2793 rb.Push(ResultSuccess);
2794 rb.Push(static_cast<u32>(unique_pads.size()));
2795 }
2796
2797 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
2798 LOG_INFO(Service_AM, "called");
2799
2800 IPC::ResponseBuilder rb{ctx, 2, 1};
2801 rb.Push(ResultSuccess);
2802 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
2803 }
2804
2805 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
2806 const bool is_enabled = false;
2807
2808 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
2809
2810 IPC::ResponseBuilder rb{ctx, 3};
2811 rb.Push(ResultSuccess);
2812 rb.Push(is_enabled);
2813 }
2814
2815 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
2816 LOG_WARNING(Service_HID, "(STUBBED) called");
2817
2818 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
2819 .mode = Core::HID::TouchScreenModeForNx::Finger,
2820 };
2821
2822 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
2823 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
2824 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
2825 }
2826
2827 IPC::ResponseBuilder rb{ctx, 6};
2828 rb.Push(ResultSuccess);
2829 rb.PushRaw(touchscreen_config);
2830 }
2831
2832 std::shared_ptr<IAppletResource> GetAppletResource() {
2833 if (applet_resource == nullptr) {
2834 applet_resource = std::make_shared<IAppletResource>(system, service_context);
2835 }
2836
2837 return applet_resource;
2838 }
2839
2840 Kernel::KEvent* joy_detach_event;
2841 KernelHelpers::ServiceContext service_context;
2842 std::shared_ptr<IAppletResource> applet_resource;
2843};
2844
2845void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
2846 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
2847 std::shared_ptr<IAppletResource> applet_resource; 19 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
20 std::shared_ptr<HidFirmwareSettings> firmware_settings =
21 std::make_shared<HidFirmwareSettings>();
22
23 server_manager->RegisterNamedService(
24 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
25 server_manager->RegisterNamedService(
26 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
27 server_manager->RegisterNamedService(
28 "hid:sys", std::make_shared<IHidSystemServer>(system, resouce_manager));
2848 29
2849 server_manager->RegisterNamedService("hid", std::make_shared<Hid>(system, applet_resource));
2850 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system)); 30 server_manager->RegisterNamedService("hidbus", std::make_shared<HidBus>(system));
2851 server_manager->RegisterNamedService("hid:dbg", std::make_shared<HidDbg>(system));
2852 server_manager->RegisterNamedService("hid:sys",
2853 std::make_shared<HidSys>(system, applet_resource));
2854 31
2855 server_manager->RegisterNamedService("irs", std::make_shared<Service::IRS::IRS>(system)); 32 server_manager->RegisterNamedService("irs", std::make_shared<IRS::IRS>(system));
2856 server_manager->RegisterNamedService("irs:sys", 33 server_manager->RegisterNamedService("irs:sys", std::make_shared<IRS::IRS_SYS>(system));
2857 std::make_shared<Service::IRS::IRS_SYS>(system));
2858 34
2859 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system)); 35 server_manager->RegisterNamedService("xcd:sys", std::make_shared<XCD_SYS>(system));
36
2860 system.RunServer(std::move(server_manager)); 37 system.RunServer(std::move(server_manager));
2861} 38}
2862 39
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 0ca43de93..ec5463f4e 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -3,220 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <chrono> 6namespace Core {
7 7class System;
8#include "core/hle/service/hid/controllers/controller_base.h"
9#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/service.h"
11
12namespace Core::Timing {
13struct EventType;
14}
15
16namespace Service::SM {
17class ServiceManager;
18} 8}
19 9
20namespace Service::HID { 10namespace Service::HID {
21 11
22enum class HidController : std::size_t {
23 DebugPad,
24 Touchscreen,
25 Mouse,
26 Keyboard,
27 XPad,
28 HomeButton,
29 SleepButton,
30 CaptureButton,
31 InputDetector,
32 UniquePad,
33 NPad,
34 Gesture,
35 ConsoleSixAxisSensor,
36 DebugMouse,
37 Palma,
38
39 MaxControllers,
40};
41
42class IAppletResource final : public ServiceFramework<IAppletResource> {
43public:
44 explicit IAppletResource(Core::System& system_,
45 KernelHelpers::ServiceContext& service_context_);
46 ~IAppletResource() override;
47
48 void ActivateController(HidController controller);
49 void DeactivateController(HidController controller);
50
51 template <typename T>
52 T& GetController(HidController controller) {
53 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
54 }
55
56 template <typename T>
57 const T& GetController(HidController controller) const {
58 return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
59 }
60
61private:
62 template <typename T>
63 void MakeController(HidController controller, u8* shared_memory) {
64 if constexpr (std::is_constructible_v<T, Core::System&, u8*>) {
65 controllers[static_cast<std::size_t>(controller)] =
66 std::make_unique<T>(system, shared_memory);
67 } else {
68 controllers[static_cast<std::size_t>(controller)] =
69 std::make_unique<T>(system.HIDCore(), shared_memory);
70 }
71 }
72
73 template <typename T>
74 void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
75 controllers[static_cast<std::size_t>(controller)] =
76 std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
77 }
78
79 void GetSharedMemoryHandle(HLERequestContext& ctx);
80 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
81 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
82 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
83 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
84
85 KernelHelpers::ServiceContext& service_context;
86
87 std::shared_ptr<Core::Timing::EventType> npad_update_event;
88 std::shared_ptr<Core::Timing::EventType> default_update_event;
89 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
90 std::shared_ptr<Core::Timing::EventType> motion_update_event;
91
92 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
93 controllers{};
94};
95
96class Hid final : public ServiceFramework<Hid> {
97public:
98 explicit Hid(Core::System& system_, std::shared_ptr<IAppletResource> applet_resource_);
99 ~Hid() override;
100
101 std::shared_ptr<IAppletResource> GetAppletResource();
102
103private:
104 void CreateAppletResource(HLERequestContext& ctx);
105 void ActivateDebugPad(HLERequestContext& ctx);
106 void ActivateTouchScreen(HLERequestContext& ctx);
107 void ActivateMouse(HLERequestContext& ctx);
108 void ActivateKeyboard(HLERequestContext& ctx);
109 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
110 void ActivateXpad(HLERequestContext& ctx);
111 void GetXpadIDs(HLERequestContext& ctx);
112 void ActivateSixAxisSensor(HLERequestContext& ctx);
113 void DeactivateSixAxisSensor(HLERequestContext& ctx);
114 void StartSixAxisSensor(HLERequestContext& ctx);
115 void StopSixAxisSensor(HLERequestContext& ctx);
116 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
117 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
118 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
119 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
120 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
121 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
122 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
123 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
124 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
125 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
126 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
127 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
128 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
129 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
130 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
131 void ActivateGesture(HLERequestContext& ctx);
132 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
133 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
134 void SetSupportedNpadIdType(HLERequestContext& ctx);
135 void ActivateNpad(HLERequestContext& ctx);
136 void DeactivateNpad(HLERequestContext& ctx);
137 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
138 void DisconnectNpad(HLERequestContext& ctx);
139 void GetPlayerLedPattern(HLERequestContext& ctx);
140 void ActivateNpadWithRevision(HLERequestContext& ctx);
141 void SetNpadJoyHoldType(HLERequestContext& ctx);
142 void GetNpadJoyHoldType(HLERequestContext& ctx);
143 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
144 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
145 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
146 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
147 void StartLrAssignmentMode(HLERequestContext& ctx);
148 void StopLrAssignmentMode(HLERequestContext& ctx);
149 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
150 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
151 void SwapNpadAssignment(HLERequestContext& ctx);
152 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
153 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
154 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
155 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
156 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
157 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
158 void GetVibrationDeviceInfo(HLERequestContext& ctx);
159 void SendVibrationValue(HLERequestContext& ctx);
160 void GetActualVibrationValue(HLERequestContext& ctx);
161 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
162 void PermitVibration(HLERequestContext& ctx);
163 void IsVibrationPermitted(HLERequestContext& ctx);
164 void SendVibrationValues(HLERequestContext& ctx);
165 void SendVibrationGcErmCommand(HLERequestContext& ctx);
166 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
167 void BeginPermitVibrationSession(HLERequestContext& ctx);
168 void EndPermitVibrationSession(HLERequestContext& ctx);
169 void IsVibrationDeviceMounted(HLERequestContext& ctx);
170 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
171 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
172 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
173 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
174 void StartSevenSixAxisSensor(HLERequestContext& ctx);
175 void StopSevenSixAxisSensor(HLERequestContext& ctx);
176 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
177 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
178 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
179 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
180 void GetPalmaConnectionHandle(HLERequestContext& ctx);
181 void InitializePalma(HLERequestContext& ctx);
182 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
183 void GetPalmaOperationInfo(HLERequestContext& ctx);
184 void PlayPalmaActivity(HLERequestContext& ctx);
185 void SetPalmaFrModeType(HLERequestContext& ctx);
186 void ReadPalmaStep(HLERequestContext& ctx);
187 void EnablePalmaStep(HLERequestContext& ctx);
188 void ResetPalmaStep(HLERequestContext& ctx);
189 void ReadPalmaApplicationSection(HLERequestContext& ctx);
190 void WritePalmaApplicationSection(HLERequestContext& ctx);
191 void ReadPalmaUniqueCode(HLERequestContext& ctx);
192 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
193 void WritePalmaActivityEntry(HLERequestContext& ctx);
194 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
195 void WritePalmaWaveEntry(HLERequestContext& ctx);
196 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
197 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
198 void SuspendPalmaFeature(HLERequestContext& ctx);
199 void GetPalmaOperationResult(HLERequestContext& ctx);
200 void ReadPalmaPlayLog(HLERequestContext& ctx);
201 void ResetPalmaPlayLog(HLERequestContext& ctx);
202 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
203 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
204 void PairPalma(HLERequestContext& ctx);
205 void SetPalmaBoostMode(HLERequestContext& ctx);
206 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
207 void EnablePalmaBoostMode(HLERequestContext& ctx);
208 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
209 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
210 void SetNpadCommunicationMode(HLERequestContext& ctx);
211 void GetNpadCommunicationMode(HLERequestContext& ctx);
212 void SetTouchScreenConfiguration(HLERequestContext& ctx);
213 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
214
215 std::shared_ptr<IAppletResource> applet_resource;
216
217 KernelHelpers::ServiceContext service_context;
218};
219
220void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
221 13
222} // namespace Service::HID 14} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp
new file mode 100644
index 000000000..6294f3dfb
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.cpp
@@ -0,0 +1,159 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_debug_server.h"
5#include "core/hle/service/hid/resource_manager.h"
6#include "core/hle/service/ipc_helpers.h"
7
8namespace Service::HID {
9
10IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
11 : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} {
12 // clang-format off
13 static const FunctionInfo functions[] = {
14 {0, nullptr, "DeactivateDebugPad"},
15 {1, nullptr, "SetDebugPadAutoPilotState"},
16 {2, nullptr, "UnsetDebugPadAutoPilotState"},
17 {10, nullptr, "DeactivateTouchScreen"},
18 {11, nullptr, "SetTouchScreenAutoPilotState"},
19 {12, nullptr, "UnsetTouchScreenAutoPilotState"},
20 {13, nullptr, "GetTouchScreenConfiguration"},
21 {14, nullptr, "ProcessTouchScreenAutoTune"},
22 {15, nullptr, "ForceStopTouchScreenManagement"},
23 {16, nullptr, "ForceRestartTouchScreenManagement"},
24 {17, nullptr, "IsTouchScreenManaged"},
25 {20, nullptr, "DeactivateMouse"},
26 {21, nullptr, "SetMouseAutoPilotState"},
27 {22, nullptr, "UnsetMouseAutoPilotState"},
28 {25, nullptr, "SetDebugMouseAutoPilotState"},
29 {26, nullptr, "UnsetDebugMouseAutoPilotState"},
30 {30, nullptr, "DeactivateKeyboard"},
31 {31, nullptr, "SetKeyboardAutoPilotState"},
32 {32, nullptr, "UnsetKeyboardAutoPilotState"},
33 {50, nullptr, "DeactivateXpad"},
34 {51, nullptr, "SetXpadAutoPilotState"},
35 {52, nullptr, "UnsetXpadAutoPilotState"},
36 {53, nullptr, "DeactivateJoyXpad"},
37 {60, nullptr, "ClearNpadSystemCommonPolicy"},
38 {61, nullptr, "DeactivateNpad"},
39 {62, nullptr, "ForceDisconnectNpad"},
40 {91, nullptr, "DeactivateGesture"},
41 {110, nullptr, "DeactivateHomeButton"},
42 {111, nullptr, "SetHomeButtonAutoPilotState"},
43 {112, nullptr, "UnsetHomeButtonAutoPilotState"},
44 {120, nullptr, "DeactivateSleepButton"},
45 {121, nullptr, "SetSleepButtonAutoPilotState"},
46 {122, nullptr, "UnsetSleepButtonAutoPilotState"},
47 {123, nullptr, "DeactivateInputDetector"},
48 {130, nullptr, "DeactivateCaptureButton"},
49 {131, nullptr, "SetCaptureButtonAutoPilotState"},
50 {132, nullptr, "UnsetCaptureButtonAutoPilotState"},
51 {133, nullptr, "SetShiftAccelerometerCalibrationValue"},
52 {134, nullptr, "GetShiftAccelerometerCalibrationValue"},
53 {135, nullptr, "SetShiftGyroscopeCalibrationValue"},
54 {136, nullptr, "GetShiftGyroscopeCalibrationValue"},
55 {140, nullptr, "DeactivateConsoleSixAxisSensor"},
56 {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"},
57 {142, nullptr, "DeactivateSevenSixAxisSensor"},
58 {143, nullptr, "GetConsoleSixAxisSensorCountStates"},
59 {144, nullptr, "GetAccelerometerFsr"},
60 {145, nullptr, "SetAccelerometerFsr"},
61 {146, nullptr, "GetAccelerometerOdr"},
62 {147, nullptr, "SetAccelerometerOdr"},
63 {148, nullptr, "GetGyroscopeFsr"},
64 {149, nullptr, "SetGyroscopeFsr"},
65 {150, nullptr, "GetGyroscopeOdr"},
66 {151, nullptr, "SetGyroscopeOdr"},
67 {152, nullptr, "GetWhoAmI"},
68 {201, nullptr, "ActivateFirmwareUpdate"},
69 {202, nullptr, "DeactivateFirmwareUpdate"},
70 {203, nullptr, "StartFirmwareUpdate"},
71 {204, nullptr, "GetFirmwareUpdateStage"},
72 {205, nullptr, "GetFirmwareVersion"},
73 {206, nullptr, "GetDestinationFirmwareVersion"},
74 {207, nullptr, "DiscardFirmwareInfoCacheForRevert"},
75 {208, nullptr, "StartFirmwareUpdateForRevert"},
76 {209, nullptr, "GetAvailableFirmwareVersionForRevert"},
77 {210, nullptr, "IsFirmwareUpdatingDevice"},
78 {211, nullptr, "StartFirmwareUpdateIndividual"},
79 {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"},
80 {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"},
81 {221, nullptr, "UpdateControllerColor"},
82 {222, nullptr, "ConnectUsbPadsAsync"},
83 {223, nullptr, "DisconnectUsbPadsAsync"},
84 {224, nullptr, "UpdateDesignInfo"},
85 {225, nullptr, "GetUniquePadDriverState"},
86 {226, nullptr, "GetSixAxisSensorDriverStates"},
87 {227, nullptr, "GetRxPacketHistory"},
88 {228, nullptr, "AcquireOperationEventHandle"},
89 {229, nullptr, "ReadSerialFlash"},
90 {230, nullptr, "WriteSerialFlash"},
91 {231, nullptr, "GetOperationResult"},
92 {232, nullptr, "EnableShipmentMode"},
93 {233, nullptr, "ClearPairingInfo"},
94 {234, nullptr, "GetUniquePadDeviceTypeSetInternal"},
95 {235, nullptr, "EnableAnalogStickPower"},
96 {236, nullptr, "RequestKuinaUartClockCal"},
97 {237, nullptr, "GetKuinaUartClockCal"},
98 {238, nullptr, "SetKuinaUartClockTrim"},
99 {239, nullptr, "KuinaLoopbackTest"},
100 {240, nullptr, "RequestBatteryVoltage"},
101 {241, nullptr, "GetBatteryVoltage"},
102 {242, nullptr, "GetUniquePadPowerInfo"},
103 {243, nullptr, "RebootUniquePad"},
104 {244, nullptr, "RequestKuinaFirmwareVersion"},
105 {245, nullptr, "GetKuinaFirmwareVersion"},
106 {246, nullptr, "GetVidPid"},
107 {247, nullptr, "GetAnalogStickCalibrationValue"},
108 {248, nullptr, "GetUniquePadIdsFull"},
109 {249, nullptr, "ConnectUniquePad"},
110 {250, nullptr, "IsVirtual"},
111 {251, nullptr, "GetAnalogStickModuleParam"},
112 {301, nullptr, "GetAbstractedPadHandles"},
113 {302, nullptr, "GetAbstractedPadState"},
114 {303, nullptr, "GetAbstractedPadsState"},
115 {321, nullptr, "SetAutoPilotVirtualPadState"},
116 {322, nullptr, "UnsetAutoPilotVirtualPadState"},
117 {323, nullptr, "UnsetAllAutoPilotVirtualPadState"},
118 {324, nullptr, "AttachHdlsWorkBuffer"},
119 {325, nullptr, "ReleaseHdlsWorkBuffer"},
120 {326, nullptr, "DumpHdlsNpadAssignmentState"},
121 {327, nullptr, "DumpHdlsStates"},
122 {328, nullptr, "ApplyHdlsNpadAssignmentState"},
123 {329, nullptr, "ApplyHdlsStateList"},
124 {330, nullptr, "AttachHdlsVirtualDevice"},
125 {331, nullptr, "DetachHdlsVirtualDevice"},
126 {332, nullptr, "SetHdlsState"},
127 {350, nullptr, "AddRegisteredDevice"},
128 {400, nullptr, "DisableExternalMcuOnNxDevice"},
129 {401, nullptr, "DisableRailDeviceFiltering"},
130 {402, nullptr, "EnableWiredPairing"},
131 {403, nullptr, "EnableShipmentModeAutoClear"},
132 {404, nullptr, "SetRailEnabled"},
133 {500, nullptr, "SetFactoryInt"},
134 {501, nullptr, "IsFactoryBootEnabled"},
135 {550, nullptr, "SetAnalogStickModelDataTemporarily"},
136 {551, nullptr, "GetAnalogStickModelData"},
137 {552, nullptr, "ResetAnalogStickModelData"},
138 {600, nullptr, "ConvertPadState"},
139 {650, nullptr, "AddButtonPlayData"},
140 {651, nullptr, "StartButtonPlayData"},
141 {652, nullptr, "StopButtonPlayData"},
142 {2000, nullptr, "DeactivateDigitizer"},
143 {2001, nullptr, "SetDigitizerAutoPilotState"},
144 {2002, nullptr, "UnsetDigitizerAutoPilotState"},
145 {2002, nullptr, "ReloadFirmwareDebugSettings"},
146 };
147 // clang-format on
148
149 RegisterHandlers(functions);
150}
151
152IHidDebugServer::~IHidDebugServer() = default;
153
154std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() {
155 resource_manager->Initialize();
156 return resource_manager;
157}
158
159} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h
new file mode 100644
index 000000000..406db2211
--- /dev/null
+++ b/src/core/hle/service/hid/hid_debug_server.h
@@ -0,0 +1,26 @@
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/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14
15class IHidDebugServer final : public ServiceFramework<IHidDebugServer> {
16public:
17 explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
18 ~IHidDebugServer() override;
19
20private:
21 std::shared_ptr<ResourceManager> GetResourceManager();
22
23 std::shared_ptr<ResourceManager> resource_manager;
24};
25
26} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp
new file mode 100644
index 000000000..59bd6825c
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_firmware_settings.h"
5
6namespace Service::HID {
7
8HidFirmwareSettings::HidFirmwareSettings() {
9 LoadSettings(true);
10}
11
12void HidFirmwareSettings::Reload() {
13 LoadSettings(true);
14}
15
16void HidFirmwareSettings::LoadSettings(bool reload_config) {
17 if (is_initalized && !reload_config) {
18 return;
19 }
20
21 // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
22
23 is_debug_pad_enabled = true;
24 is_device_managed = true;
25 is_touch_i2c_managed = is_device_managed;
26 is_future_devices_emulated = false;
27 is_mcu_hardware_error_emulated = false;
28 is_rail_enabled = true;
29 is_firmware_update_failure_emulated = false;
30 is_firmware_update_failure = {};
31 is_ble_disabled = false;
32 is_dscale_disabled = false;
33 is_handheld_forced = true;
34 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false;
36 is_initalized = true;
37}
38
39bool HidFirmwareSettings::IsDebugPadEnabled() {
40 LoadSettings(false);
41 return is_debug_pad_enabled;
42}
43
44bool HidFirmwareSettings::IsDeviceManaged() {
45 LoadSettings(false);
46 return is_device_managed;
47}
48
49bool HidFirmwareSettings::IsEmulateFutureDevice() {
50 LoadSettings(false);
51 return is_future_devices_emulated;
52}
53
54bool HidFirmwareSettings::IsTouchI2cManaged() {
55 LoadSettings(false);
56 return is_touch_i2c_managed;
57}
58
59bool HidFirmwareSettings::IsHandheldForced() {
60 LoadSettings(false);
61 return is_handheld_forced;
62}
63
64bool HidFirmwareSettings::IsRailEnabled() {
65 LoadSettings(false);
66 return is_rail_enabled;
67}
68
69bool HidFirmwareSettings::IsHardwareErrorEmulated() {
70 LoadSettings(false);
71 return is_mcu_hardware_error_emulated;
72}
73
74bool HidFirmwareSettings::IsBleDisabled() {
75 LoadSettings(false);
76 return is_ble_disabled;
77}
78
79bool HidFirmwareSettings::IsDscaleDisabled() {
80 LoadSettings(false);
81 return is_dscale_disabled;
82}
83
84bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
85 LoadSettings(false);
86 return is_touch_firmware_auto_update_disabled;
87}
88
89HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
90 LoadSettings(false);
91 return is_firmware_update_failure;
92}
93
94HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
95 LoadSettings(false);
96 return features_per_id_disabled;
97}
98
99} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h
new file mode 100644
index 000000000..6c10c440b
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.h
@@ -0,0 +1,54 @@
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
8namespace Service::HID {
9
10/// Loads firmware config from nn::settings::fwdbg
11class HidFirmwareSettings {
12public:
13 using FirmwareSetting = std::array<u8, 4>;
14 using FeaturesPerId = std::array<bool, 0xA8>;
15
16 HidFirmwareSettings();
17
18 void Reload();
19 void LoadSettings(bool reload_config);
20
21 bool IsDebugPadEnabled();
22 bool IsDeviceManaged();
23 bool IsEmulateFutureDevice();
24 bool IsTouchI2cManaged();
25 bool IsHandheldForced();
26 bool IsRailEnabled();
27 bool IsHardwareErrorEmulated();
28 bool IsBleDisabled();
29 bool IsDscaleDisabled();
30 bool IsTouchAutoUpdateDisabled();
31
32 FirmwareSetting GetFirmwareUpdateFailure();
33 FeaturesPerId FeaturesDisabledPerId();
34
35private:
36 bool is_initalized{};
37
38 // Debug settings
39 bool is_debug_pad_enabled{};
40 bool is_device_managed{};
41 bool is_touch_i2c_managed{};
42 bool is_future_devices_emulated{};
43 bool is_mcu_hardware_error_emulated{};
44 bool is_rail_enabled{};
45 bool is_firmware_update_failure_emulated{};
46 bool is_ble_disabled{};
47 bool is_dscale_disabled{};
48 bool is_handheld_forced{};
49 bool is_touch_firmware_auto_update_disabled{};
50 FirmwareSetting is_firmware_update_failure{};
51 FeaturesPerId features_per_id_disabled{};
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
new file mode 100644
index 000000000..583142e35
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -0,0 +1,2371 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include <array>
5#include "common/common_types.h"
6#include "common/logging/log.h"
7#include "common/settings.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/kernel/k_shared_memory.h"
10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/hid/errors.h"
13#include "core/hle/service/hid/hid_firmware_settings.h"
14#include "core/hle/service/hid/hid_server.h"
15#include "core/hle/service/hid/hid_util.h"
16#include "core/hle/service/hid/resource_manager.h"
17#include "core/hle/service/ipc_helpers.h"
18#include "core/memory.h"
19
20#include "core/hle/service/hid/controllers/console_six_axis.h"
21#include "core/hle/service/hid/controllers/controller_base.h"
22#include "core/hle/service/hid/controllers/debug_pad.h"
23#include "core/hle/service/hid/controllers/gesture.h"
24#include "core/hle/service/hid/controllers/keyboard.h"
25#include "core/hle/service/hid/controllers/mouse.h"
26#include "core/hle/service/hid/controllers/npad.h"
27#include "core/hle/service/hid/controllers/palma.h"
28#include "core/hle/service/hid/controllers/seven_six_axis.h"
29#include "core/hle/service/hid/controllers/six_axis.h"
30#include "core/hle/service/hid/controllers/touchscreen.h"
31
32namespace Service::HID {
33
34class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
35public:
36 explicit IActiveVibrationDeviceList(Core::System& system_,
37 std::shared_ptr<ResourceManager> resource)
38 : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) {
39 // clang-format off
40 static const FunctionInfo functions[] = {
41 {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
42 };
43 // clang-format on
44
45 RegisterHandlers(functions);
46 }
47
48private:
49 void InitializeVibrationDevice(HLERequestContext& ctx) {
50 IPC::RequestParser rp{ctx};
51 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
52
53 if (resource_manager != nullptr) {
54 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
55 }
56
57 LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
58 vibration_device_handle.npad_type, vibration_device_handle.npad_id,
59 vibration_device_handle.device_index);
60
61 IPC::ResponseBuilder rb{ctx, 2};
62 rb.Push(ResultSuccess);
63 }
64
65 std::shared_ptr<ResourceManager> resource_manager;
66};
67
68IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
69 std::shared_ptr<HidFirmwareSettings> settings)
70 : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} {
71 // clang-format off
72 static const FunctionInfo functions[] = {
73 {0, &IHidServer::CreateAppletResource, "CreateAppletResource"},
74 {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"},
75 {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"},
76 {21, &IHidServer::ActivateMouse, "ActivateMouse"},
77 {26, nullptr, "ActivateDebugMouse"},
78 {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"},
79 {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"},
80 {40, &IHidServer::AcquireXpadIdEventHandle, "AcquireXpadIdEventHandle"},
81 {41, &IHidServer::ReleaseXpadIdEventHandle, "ReleaseXpadIdEventHandle"},
82 {51, &IHidServer::ActivateXpad, "ActivateXpad"},
83 {55, &IHidServer::GetXpadIds, "GetXpadIds"},
84 {56, &IHidServer::ActivateJoyXpad, "ActivateJoyXpad"},
85 {58, &IHidServer::GetJoyXpadLifoHandle, "GetJoyXpadLifoHandle"},
86 {59, &IHidServer::GetJoyXpadIds, "GetJoyXpadIds"},
87 {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
88 {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
89 {62, &IHidServer::GetSixAxisSensorLifoHandle, "GetSixAxisSensorLifoHandle"},
90 {63, &IHidServer::ActivateJoySixAxisSensor, "ActivateJoySixAxisSensor"},
91 {64, &IHidServer::DeactivateJoySixAxisSensor, "DeactivateJoySixAxisSensor"},
92 {65, &IHidServer::GetJoySixAxisSensorLifoHandle, "GetJoySixAxisSensorLifoHandle"},
93 {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"},
94 {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"},
95 {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
96 {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
97 {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
98 {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
99 {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"},
100 {73, nullptr, "SetAccelerometerParameters"},
101 {74, nullptr, "GetAccelerometerParameters"},
102 {75, nullptr, "ResetAccelerometerParameters"},
103 {76, nullptr, "SetAccelerometerPlayMode"},
104 {77, nullptr, "GetAccelerometerPlayMode"},
105 {78, nullptr, "ResetAccelerometerPlayMode"},
106 {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
107 {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"},
108 {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
109 {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
110 {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
111 {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
112 {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
113 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
114 {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
115 {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
116 {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
117 {91, &IHidServer::ActivateGesture, "ActivateGesture"},
118 {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
119 {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
120 {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
121 {103, &IHidServer::ActivateNpad, "ActivateNpad"},
122 {104, &IHidServer::DeactivateNpad, "DeactivateNpad"},
123 {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
124 {107, &IHidServer::DisconnectNpad, "DisconnectNpad"},
125 {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"},
126 {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
127 {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
128 {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
129 {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
130 {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"},
131 {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
132 {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
133 {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"},
134 {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"},
135 {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
136 {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
137 {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"},
138 {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
139 {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
140 {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"},
141 {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"},
142 {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"},
143 {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"},
144 {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
145 {201, &IHidServer::SendVibrationValue, "SendVibrationValue"},
146 {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"},
147 {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
148 {204, &IHidServer::PermitVibration, "PermitVibration"},
149 {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"},
150 {206, &IHidServer::SendVibrationValues, "SendVibrationValues"},
151 {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"},
152 {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"},
153 {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
154 {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"},
155 {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
156 {212, nullptr, "SendVibrationValueInBool"},
157 {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
158 {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
159 {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
160 {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"},
161 {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"},
162 {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"},
163 {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
164 {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"},
165 {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
166 {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
167 {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"},
168 {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
169 {401, nullptr, "EnableUsbFullKeyController"},
170 {402, nullptr, "IsUsbFullKeyControllerConnected"},
171 {403, nullptr, "HasBattery"},
172 {404, nullptr, "HasLeftRightBattery"},
173 {405, nullptr, "GetNpadInterfaceType"},
174 {406, nullptr, "GetNpadLeftRightInterfaceType"},
175 {407, nullptr, "GetNpadOfHighestBatteryLevel"},
176 {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
177 {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
178 {501, &IHidServer::InitializePalma, "InitializePalma"},
179 {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
180 {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
181 {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"},
182 {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"},
183 {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"},
184 {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"},
185 {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"},
186 {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
187 {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
188 {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
189 {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
190 {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
191 {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
192 {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
193 {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
194 {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
195 {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"},
196 {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"},
197 {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
198 {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
199 {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
200 {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
201 {524, &IHidServer::PairPalma, "PairPalma"},
202 {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"},
203 {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
204 {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
205 {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
206 {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
207 {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
208 {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
209 {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
210 {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
211 {2000, nullptr, "ActivateDigitizer"},
212 };
213 // clang-format on
214
215 RegisterHandlers(functions);
216}
217
218IHidServer::~IHidServer() = default;
219
220void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
221 IPC::RequestParser rp{ctx};
222 const auto applet_resource_user_id{rp.Pop<u64>()};
223
224 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
225
226 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
227 rb.Push(ResultSuccess);
228 rb.PushIpcInterface<IAppletResource>(system, resource_manager);
229}
230
231void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
232 IPC::RequestParser rp{ctx};
233 const auto applet_resource_user_id{rp.Pop<u64>()};
234
235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
236
237 Result result = ResultSuccess;
238 auto debug_pad = GetResourceManager()->GetDebugPad();
239
240 if (!firmware_settings->IsDeviceManaged()) {
241 result = debug_pad->Activate();
242 }
243
244 if (result.IsSuccess()) {
245 result = debug_pad->Activate(applet_resource_user_id);
246 }
247
248 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(result);
250}
251
252void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
253 IPC::RequestParser rp{ctx};
254 const auto applet_resource_user_id{rp.Pop<u64>()};
255
256 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
257
258 Result result = ResultSuccess;
259 auto touch_screen = GetResourceManager()->GetTouchScreen();
260
261 if (!firmware_settings->IsDeviceManaged()) {
262 result = touch_screen->Activate();
263 }
264
265 if (result.IsSuccess()) {
266 result = touch_screen->Activate(applet_resource_user_id);
267 }
268
269 IPC::ResponseBuilder rb{ctx, 2};
270 rb.Push(result);
271}
272
273void IHidServer::ActivateMouse(HLERequestContext& ctx) {
274 IPC::RequestParser rp{ctx};
275 const auto applet_resource_user_id{rp.Pop<u64>()};
276
277 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
278
279 Result result = ResultSuccess;
280 auto mouse = GetResourceManager()->GetMouse();
281
282 if (!firmware_settings->IsDeviceManaged()) {
283 result = mouse->Activate();
284 }
285
286 if (result.IsSuccess()) {
287 result = mouse->Activate(applet_resource_user_id);
288 }
289
290 IPC::ResponseBuilder rb{ctx, 2};
291 rb.Push(result);
292}
293
294void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 const auto applet_resource_user_id{rp.Pop<u64>()};
297
298 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
299
300 Result result = ResultSuccess;
301 auto keyboard = GetResourceManager()->GetKeyboard();
302
303 if (!firmware_settings->IsDeviceManaged()) {
304 result = keyboard->Activate();
305 }
306
307 if (result.IsSuccess()) {
308 result = keyboard->Activate(applet_resource_user_id);
309 }
310
311 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(result);
313}
314
315void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
316 IPC::RequestParser rp{ctx};
317 const auto flags{rp.Pop<u32>()};
318
319 LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323}
324
325void IHidServer::AcquireXpadIdEventHandle(HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx};
327 const auto applet_resource_user_id{rp.Pop<u64>()};
328
329 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
330
331 // This function has been stubbed since 10.0.0+
332
333 IPC::ResponseBuilder rb{ctx, 2, 1};
334 rb.Push(ResultSuccess);
335 // Handle returned is null here
336}
337
338void IHidServer::ReleaseXpadIdEventHandle(HLERequestContext& ctx) {
339 IPC::RequestParser rp{ctx};
340 const auto applet_resource_user_id{rp.Pop<u64>()};
341
342 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
343
344 // This function has been stubbed since 10.0.0+
345
346 IPC::ResponseBuilder rb{ctx, 2};
347 rb.Push(ResultSuccess);
348}
349
350void IHidServer::ActivateXpad(HLERequestContext& ctx) {
351 IPC::RequestParser rp{ctx};
352 struct Parameters {
353 u32 basic_xpad_id;
354 INSERT_PADDING_WORDS_NOINIT(1);
355 u64 applet_resource_user_id;
356 };
357 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
358
359 const auto parameters{rp.PopRaw<Parameters>()};
360
361 LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
362 parameters.basic_xpad_id, parameters.applet_resource_user_id);
363
364 // This function has been stubbed since 10.0.0+
365
366 IPC::ResponseBuilder rb{ctx, 2};
367 rb.Push(ResultSuccess);
368}
369
370void IHidServer::GetXpadIds(HLERequestContext& ctx) {
371 LOG_DEBUG(Service_HID, "called");
372
373 // This function has been hardcoded since 10.0.0+
374 const std::array<u32, 4> basic_xpad_id{0, 1, 2, 3};
375 ctx.WriteBuffer(basic_xpad_id);
376
377 IPC::ResponseBuilder rb{ctx, 4};
378 rb.Push(ResultSuccess);
379 rb.Push<s64>(basic_xpad_id.size());
380}
381
382void IHidServer::ActivateJoyXpad(HLERequestContext& ctx) {
383 IPC::RequestParser rp{ctx};
384 const auto joy_xpad_id{rp.Pop<u32>()};
385
386 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
387
388 // This function has been stubbed since 10.0.0+
389
390 IPC::ResponseBuilder rb{ctx, 2};
391 rb.Push(ResultSuccess);
392}
393
394void IHidServer::GetJoyXpadLifoHandle(HLERequestContext& ctx) {
395 IPC::RequestParser rp{ctx};
396 const auto joy_xpad_id{rp.Pop<u32>()};
397
398 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
399
400 // This function has been stubbed since 10.0.0+
401
402 IPC::ResponseBuilder rb{ctx, 2, 1};
403 rb.Push(ResultSuccess);
404 // Handle returned is null here
405}
406
407void IHidServer::GetJoyXpadIds(HLERequestContext& ctx) {
408 LOG_DEBUG(Service_HID, "called");
409
410 // This function has been hardcoded since 10.0.0+
411 const s64 basic_xpad_id_count{};
412
413 IPC::ResponseBuilder rb{ctx, 4};
414 rb.Push(ResultSuccess);
415 rb.Push(basic_xpad_id_count);
416}
417
418void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) {
419 IPC::RequestParser rp{ctx};
420 const auto joy_xpad_id{rp.Pop<u32>()};
421
422 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
423
424 // This function has been stubbed since 10.0.0+
425
426 IPC::ResponseBuilder rb{ctx, 2};
427 rb.Push(ResultSuccess);
428}
429
430void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) {
431 IPC::RequestParser rp{ctx};
432 const auto joy_xpad_id{rp.Pop<u32>()};
433
434 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
435
436 // This function has been stubbed since 10.0.0+
437
438 IPC::ResponseBuilder rb{ctx, 2, 1};
439 rb.Push(ResultSuccess);
440}
441
442void IHidServer::GetSixAxisSensorLifoHandle(HLERequestContext& ctx) {
443 IPC::RequestParser rp{ctx};
444 const auto joy_xpad_id{rp.Pop<u32>()};
445
446 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
447
448 // This function has been stubbed since 10.0.0+
449
450 IPC::ResponseBuilder rb{ctx, 2};
451 rb.Push(ResultSuccess);
452}
453
454void IHidServer::ActivateJoySixAxisSensor(HLERequestContext& ctx) {
455 IPC::RequestParser rp{ctx};
456 const auto joy_xpad_id{rp.Pop<u32>()};
457
458 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
459
460 // This function has been stubbed since 10.0.0+
461
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(ResultSuccess);
464}
465
466void IHidServer::DeactivateJoySixAxisSensor(HLERequestContext& ctx) {
467 IPC::RequestParser rp{ctx};
468 const auto joy_xpad_id{rp.Pop<u32>()};
469
470 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
471
472 // This function has been stubbed since 10.0.0+
473
474 IPC::ResponseBuilder rb{ctx, 2};
475 rb.Push(ResultSuccess);
476}
477
478void IHidServer::GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx) {
479 IPC::RequestParser rp{ctx};
480 const auto joy_xpad_id{rp.Pop<u32>()};
481
482 LOG_DEBUG(Service_HID, "called, joy_xpad_id={}", joy_xpad_id);
483
484 // This function has been stubbed since 10.0.0+
485
486 IPC::ResponseBuilder rb{ctx, 2, 1};
487 rb.Push(ResultSuccess);
488 // Handle returned is null here
489}
490
491void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) {
492 IPC::RequestParser rp{ctx};
493 struct Parameters {
494 Core::HID::SixAxisSensorHandle sixaxis_handle;
495 INSERT_PADDING_WORDS_NOINIT(1);
496 u64 applet_resource_user_id;
497 };
498 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
499
500 const auto parameters{rp.PopRaw<Parameters>()};
501
502 auto six_axis = GetResourceManager()->GetSixAxis();
503 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, true);
504
505 LOG_DEBUG(Service_HID,
506 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
507 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
508 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
509
510 IPC::ResponseBuilder rb{ctx, 2};
511 rb.Push(result);
512}
513
514void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) {
515 IPC::RequestParser rp{ctx};
516 struct Parameters {
517 Core::HID::SixAxisSensorHandle sixaxis_handle;
518 INSERT_PADDING_WORDS_NOINIT(1);
519 u64 applet_resource_user_id;
520 };
521 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
522
523 const auto parameters{rp.PopRaw<Parameters>()};
524
525 auto six_axis = GetResourceManager()->GetSixAxis();
526 const auto result = six_axis->SetSixAxisEnabled(parameters.sixaxis_handle, false);
527
528 LOG_DEBUG(Service_HID,
529 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
530 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
531 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
532
533 IPC::ResponseBuilder rb{ctx, 2};
534 rb.Push(result);
535}
536
537void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) {
538 IPC::RequestParser rp{ctx};
539 struct Parameters {
540 Core::HID::SixAxisSensorHandle sixaxis_handle;
541 INSERT_PADDING_WORDS_NOINIT(1);
542 u64 applet_resource_user_id;
543 };
544 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
545
546 const auto parameters{rp.PopRaw<Parameters>()};
547
548 bool is_enabled{};
549 auto six_axis = GetResourceManager()->GetSixAxis();
550 const auto result =
551 six_axis->IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
552
553 LOG_DEBUG(Service_HID,
554 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
555 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
556 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
557
558 IPC::ResponseBuilder rb{ctx, 3};
559 rb.Push(result);
560 rb.Push(is_enabled);
561}
562
563void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) {
564 IPC::RequestParser rp{ctx};
565 struct Parameters {
566 bool enable_sixaxis_sensor_fusion;
567 INSERT_PADDING_BYTES_NOINIT(3);
568 Core::HID::SixAxisSensorHandle sixaxis_handle;
569 u64 applet_resource_user_id;
570 };
571 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
572
573 const auto parameters{rp.PopRaw<Parameters>()};
574
575 auto six_axis = GetResourceManager()->GetSixAxis();
576 const auto result = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle,
577 parameters.enable_sixaxis_sensor_fusion);
578
579 LOG_DEBUG(Service_HID,
580 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
581 "device_index={}, applet_resource_user_id={}",
582 parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
583 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
584 parameters.applet_resource_user_id);
585
586 IPC::ResponseBuilder rb{ctx, 2};
587 rb.Push(result);
588}
589
590void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
591 IPC::RequestParser rp{ctx};
592 struct Parameters {
593 Core::HID::SixAxisSensorHandle sixaxis_handle;
594 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion;
595 INSERT_PADDING_WORDS_NOINIT(1);
596 u64 applet_resource_user_id;
597 };
598 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
599
600 const auto parameters{rp.PopRaw<Parameters>()};
601
602 auto six_axis = GetResourceManager()->GetSixAxis();
603 const auto result =
604 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
605
606 LOG_DEBUG(Service_HID,
607 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
608 "parameter2={}, applet_resource_user_id={}",
609 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
610 parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1,
611 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
612
613 IPC::ResponseBuilder rb{ctx, 2};
614 rb.Push(result);
615}
616
617void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
618 IPC::RequestParser rp{ctx};
619 struct Parameters {
620 Core::HID::SixAxisSensorHandle sixaxis_handle;
621 INSERT_PADDING_WORDS_NOINIT(1);
622 u64 applet_resource_user_id;
623 };
624 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
625
626 const auto parameters{rp.PopRaw<Parameters>()};
627
628 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
629 auto six_axis = GetResourceManager()->GetSixAxis();
630 const auto result =
631 six_axis->GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632
633 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
635 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637
638 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(result);
640 rb.PushRaw(fusion_parameters);
641}
642
643void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) {
644 IPC::RequestParser rp{ctx};
645 struct Parameters {
646 Core::HID::SixAxisSensorHandle sixaxis_handle;
647 INSERT_PADDING_WORDS_NOINIT(1);
648 u64 applet_resource_user_id;
649 };
650 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
651
652 const auto parameters{rp.PopRaw<Parameters>()};
653
654 // Since these parameters are unknown just use what HW outputs
655 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
656 .parameter1 = 0.03f,
657 .parameter2 = 0.4f,
658 };
659 auto six_axis = GetResourceManager()->GetSixAxis();
660 const auto result1 =
661 six_axis->SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
662 const auto result2 = six_axis->SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
663
664 LOG_DEBUG(Service_HID,
665 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
666 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
667 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
668
669 IPC::ResponseBuilder rb{ctx, 2};
670 if (result1.IsError()) {
671 rb.Push(result1);
672 return;
673 }
674 rb.Push(result2);
675}
676
677void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
678 IPC::RequestParser rp{ctx};
679 const auto sixaxis_handle{rp.PopRaw<Core::HID::SixAxisSensorHandle>()};
680 const auto drift_mode{rp.PopEnum<Core::HID::GyroscopeZeroDriftMode>()};
681 const auto applet_resource_user_id{rp.Pop<u64>()};
682
683 auto six_axis = GetResourceManager()->GetSixAxis();
684 const auto result = six_axis->SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
685
686 LOG_DEBUG(Service_HID,
687 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
688 "applet_resource_user_id={}",
689 sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
690 drift_mode, applet_resource_user_id);
691
692 IPC::ResponseBuilder rb{ctx, 2};
693 rb.Push(result);
694}
695
696void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
697 IPC::RequestParser rp{ctx};
698 struct Parameters {
699 Core::HID::SixAxisSensorHandle sixaxis_handle;
700 INSERT_PADDING_WORDS_NOINIT(1);
701 u64 applet_resource_user_id;
702 };
703 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
704
705 const auto parameters{rp.PopRaw<Parameters>()};
706
707 auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
708 auto six_axis = GetResourceManager()->GetSixAxis();
709 const auto result = six_axis->GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
710
711 LOG_DEBUG(Service_HID,
712 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
713 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
714 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
715
716 IPC::ResponseBuilder rb{ctx, 3};
717 rb.Push(result);
718 rb.PushEnum(drift_mode);
719}
720
721void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) {
722 IPC::RequestParser rp{ctx};
723 struct Parameters {
724 Core::HID::SixAxisSensorHandle sixaxis_handle;
725 INSERT_PADDING_WORDS_NOINIT(1);
726 u64 applet_resource_user_id;
727 };
728 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
729
730 const auto parameters{rp.PopRaw<Parameters>()};
731
732 const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard};
733 auto six_axis = GetResourceManager()->GetSixAxis();
734 const auto result = six_axis->SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
735
736 LOG_DEBUG(Service_HID,
737 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
738 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
739 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
740
741 IPC::ResponseBuilder rb{ctx, 2};
742 rb.Push(result);
743}
744
745void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) {
746 IPC::RequestParser rp{ctx};
747 struct Parameters {
748 Core::HID::SixAxisSensorHandle sixaxis_handle;
749 INSERT_PADDING_WORDS_NOINIT(1);
750 u64 applet_resource_user_id;
751 };
752 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
753
754 const auto parameters{rp.PopRaw<Parameters>()};
755
756 bool is_at_rest{};
757 auto six_axis = GetResourceManager()->GetSixAxis();
758 six_axis->IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
759
760 LOG_DEBUG(Service_HID,
761 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
762 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
763 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
764
765 IPC::ResponseBuilder rb{ctx, 3};
766 rb.Push(ResultSuccess);
767 rb.Push(is_at_rest);
768}
769
770void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) {
771 IPC::RequestParser rp{ctx};
772 struct Parameters {
773 Core::HID::SixAxisSensorHandle sixaxis_handle;
774 INSERT_PADDING_WORDS_NOINIT(1);
775 u64 applet_resource_user_id;
776 };
777 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
778
779 const auto parameters{rp.PopRaw<Parameters>()};
780
781 bool is_firmware_available{};
782 auto controller = GetResourceManager()->GetNpad();
783 controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
784 is_firmware_available);
785
786 LOG_WARNING(
787 Service_HID,
788 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
789 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
790 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
791
792 IPC::ResponseBuilder rb{ctx, 3};
793 rb.Push(ResultSuccess);
794 rb.Push(is_firmware_available);
795}
796
797void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) {
798 IPC::RequestParser rp{ctx};
799 struct Parameters {
800 bool enabled;
801 Core::HID::SixAxisSensorHandle sixaxis_handle;
802 u64 applet_resource_user_id;
803 };
804 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
805
806 const auto parameters{rp.PopRaw<Parameters>()};
807
808 auto six_axis = GetResourceManager()->GetSixAxis();
809 const auto result = six_axis->EnableSixAxisSensorUnalteredPassthrough(parameters.sixaxis_handle,
810 parameters.enabled);
811
812 LOG_DEBUG(Service_HID,
813 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
814 "applet_resource_user_id={}",
815 parameters.enabled, parameters.sixaxis_handle.npad_type,
816 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
817 parameters.applet_resource_user_id);
818
819 IPC::ResponseBuilder rb{ctx, 2};
820 rb.Push(result);
821}
822
823void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) {
824 IPC::RequestParser rp{ctx};
825 struct Parameters {
826 Core::HID::SixAxisSensorHandle sixaxis_handle;
827 INSERT_PADDING_WORDS_NOINIT(1);
828 u64 applet_resource_user_id;
829 };
830 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
831
832 const auto parameters{rp.PopRaw<Parameters>()};
833
834 bool is_unaltered_sisxaxis_enabled{};
835 auto six_axis = GetResourceManager()->GetSixAxis();
836 const auto result = six_axis->IsSixAxisSensorUnalteredPassthroughEnabled(
837 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
838
839 LOG_DEBUG(
840 Service_HID,
841 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
842 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
843 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
844
845 IPC::ResponseBuilder rb{ctx, 3};
846 rb.Push(result);
847 rb.Push(is_unaltered_sisxaxis_enabled);
848}
849
850void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) {
851 IPC::RequestParser rp{ctx};
852 struct Parameters {
853 Core::HID::SixAxisSensorHandle sixaxis_handle;
854 INSERT_PADDING_WORDS_NOINIT(1);
855 u64 applet_resource_user_id;
856 };
857 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
858
859 const auto parameters{rp.PopRaw<Parameters>()};
860
861 Core::HID::SixAxisSensorCalibrationParameter calibration{};
862 auto six_axis = GetResourceManager()->GetSixAxis();
863 const auto result =
864 six_axis->LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
865
866 LOG_WARNING(
867 Service_HID,
868 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
869 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
870 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
871
872 if (result.IsSuccess()) {
873 ctx.WriteBuffer(calibration);
874 }
875
876 IPC::ResponseBuilder rb{ctx, 2};
877 rb.Push(result);
878}
879
880void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) {
881 IPC::RequestParser rp{ctx};
882 struct Parameters {
883 Core::HID::SixAxisSensorHandle sixaxis_handle;
884 INSERT_PADDING_WORDS_NOINIT(1);
885 u64 applet_resource_user_id;
886 };
887 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
888
889 const auto parameters{rp.PopRaw<Parameters>()};
890
891 Core::HID::SixAxisSensorIcInformation ic_information{};
892 auto six_axis = GetResourceManager()->GetSixAxis();
893 const auto result =
894 six_axis->GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
895
896 LOG_WARNING(
897 Service_HID,
898 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
899 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
900 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
901
902 if (result.IsSuccess()) {
903 ctx.WriteBuffer(ic_information);
904 }
905
906 IPC::ResponseBuilder rb{ctx, 2};
907 rb.Push(result);
908}
909
910void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) {
911 IPC::RequestParser rp{ctx};
912 struct Parameters {
913 Core::HID::SixAxisSensorHandle sixaxis_handle;
914 INSERT_PADDING_WORDS_NOINIT(1);
915 u64 applet_resource_user_id;
916 };
917 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
918
919 const auto parameters{rp.PopRaw<Parameters>()};
920
921 auto controller = GetResourceManager()->GetNpad();
922 const auto result =
923 controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
924
925 LOG_WARNING(
926 Service_HID,
927 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
928 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
929 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
930
931 IPC::ResponseBuilder rb{ctx, 2};
932 rb.Push(result);
933}
934
935void IHidServer::ActivateGesture(HLERequestContext& ctx) {
936 IPC::RequestParser rp{ctx};
937 struct Parameters {
938 u32 basic_gesture_id;
939 INSERT_PADDING_WORDS_NOINIT(1);
940 u64 applet_resource_user_id;
941 };
942 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
943
944 const auto parameters{rp.PopRaw<Parameters>()};
945
946 LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
947 parameters.basic_gesture_id, parameters.applet_resource_user_id);
948
949 Result result = ResultSuccess;
950 auto gesture = GetResourceManager()->GetGesture();
951
952 if (!firmware_settings->IsDeviceManaged()) {
953 result = gesture->Activate();
954 }
955
956 if (result.IsSuccess()) {
957 // TODO: Use gesture id here
958 result = gesture->Activate(parameters.applet_resource_user_id);
959 }
960
961 IPC::ResponseBuilder rb{ctx, 2};
962 rb.Push(result);
963}
964
965void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
966 IPC::RequestParser rp{ctx};
967 struct Parameters {
968 Core::HID::NpadStyleSet supported_styleset;
969 INSERT_PADDING_WORDS_NOINIT(1);
970 u64 applet_resource_user_id;
971 };
972 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
973
974 const auto parameters{rp.PopRaw<Parameters>()};
975
976 GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset});
977
978 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}",
979 parameters.supported_styleset, parameters.applet_resource_user_id);
980
981 IPC::ResponseBuilder rb{ctx, 2};
982 rb.Push(ResultSuccess);
983}
984
985void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
986 IPC::RequestParser rp{ctx};
987 const auto applet_resource_user_id{rp.Pop<u64>()};
988
989 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
990
991 IPC::ResponseBuilder rb{ctx, 3};
992 rb.Push(ResultSuccess);
993 rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw);
994}
995
996void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
997 IPC::RequestParser rp{ctx};
998 const auto applet_resource_user_id{rp.Pop<u64>()};
999
1000 const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer());
1001
1002 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1003
1004 IPC::ResponseBuilder rb{ctx, 2};
1005 rb.Push(result);
1006}
1007
1008void IHidServer::ActivateNpad(HLERequestContext& ctx) {
1009 IPC::RequestParser rp{ctx};
1010 const auto applet_resource_user_id{rp.Pop<u64>()};
1011
1012 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1013
1014 auto npad = GetResourceManager()->GetNpad();
1015
1016 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1017 const Result result = npad->Activate(applet_resource_user_id);
1018
1019 IPC::ResponseBuilder rb{ctx, 2};
1020 rb.Push(result);
1021}
1022
1023void IHidServer::DeactivateNpad(HLERequestContext& ctx) {
1024 IPC::RequestParser rp{ctx};
1025 const auto applet_resource_user_id{rp.Pop<u64>()};
1026
1027 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1028
1029 // This function does nothing since 10.0.0+
1030
1031 IPC::ResponseBuilder rb{ctx, 2};
1032 rb.Push(ResultSuccess);
1033}
1034
1035void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1036 IPC::RequestParser rp{ctx};
1037 struct Parameters {
1038 Core::HID::NpadIdType npad_id;
1039 INSERT_PADDING_WORDS_NOINIT(1);
1040 u64 applet_resource_user_id;
1041 u64 unknown;
1042 };
1043 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1044
1045 const auto parameters{rp.PopRaw<Parameters>()};
1046
1047 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1048 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1049
1050 // Games expect this event to be signaled after calling this function
1051 GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id);
1052
1053 IPC::ResponseBuilder rb{ctx, 2, 1};
1054 rb.Push(ResultSuccess);
1055 rb.PushCopyObjects(
1056 GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
1057}
1058
1059void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
1060 IPC::RequestParser rp{ctx};
1061 struct Parameters {
1062 Core::HID::NpadIdType npad_id;
1063 INSERT_PADDING_WORDS_NOINIT(1);
1064 u64 applet_resource_user_id;
1065 };
1066 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1067
1068 const auto parameters{rp.PopRaw<Parameters>()};
1069
1070 auto controller = GetResourceManager()->GetNpad();
1071 controller->DisconnectNpad(parameters.npad_id);
1072
1073 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1074 parameters.applet_resource_user_id);
1075
1076 IPC::ResponseBuilder rb{ctx, 2};
1077 rb.Push(ResultSuccess);
1078}
1079
1080void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1081 IPC::RequestParser rp{ctx};
1082 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
1083
1084 Core::HID::LedPattern pattern{0, 0, 0, 0};
1085 auto controller = GetResourceManager()->GetNpad();
1086 const auto result = controller->GetLedPattern(npad_id, pattern);
1087
1088 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
1089
1090 IPC::ResponseBuilder rb{ctx, 4};
1091 rb.Push(result);
1092 rb.Push(pattern.raw);
1093}
1094
1095void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1096 IPC::RequestParser rp{ctx};
1097 struct Parameters {
1098 NPad::NpadRevision revision;
1099 INSERT_PADDING_WORDS_NOINIT(1);
1100 u64 applet_resource_user_id;
1101 };
1102 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1103
1104 const auto parameters{rp.PopRaw<Parameters>()};
1105
1106 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1107 parameters.applet_resource_user_id);
1108
1109 auto npad = GetResourceManager()->GetNpad();
1110
1111 // TODO: npad->SetRevision(applet_resource_user_id, revision);
1112 const auto result = npad->Activate(parameters.applet_resource_user_id);
1113
1114 IPC::ResponseBuilder rb{ctx, 2};
1115 rb.Push(result);
1116}
1117
1118void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1119 IPC::RequestParser rp{ctx};
1120 const auto applet_resource_user_id{rp.Pop<u64>()};
1121 const auto hold_type{rp.PopEnum<NPad::NpadJoyHoldType>()};
1122
1123 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1124
1125 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1126 applet_resource_user_id, hold_type);
1127
1128 IPC::ResponseBuilder rb{ctx, 2};
1129 rb.Push(ResultSuccess);
1130}
1131
1132void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
1133 IPC::RequestParser rp{ctx};
1134 const auto applet_resource_user_id{rp.Pop<u64>()};
1135
1136 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1137
1138 IPC::ResponseBuilder rb{ctx, 4};
1139 rb.Push(ResultSuccess);
1140 rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType());
1141}
1142
1143void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
1144 IPC::RequestParser rp{ctx};
1145 struct Parameters {
1146 Core::HID::NpadIdType npad_id;
1147 INSERT_PADDING_WORDS_NOINIT(1);
1148 u64 applet_resource_user_id;
1149 };
1150 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1151
1152 const auto parameters{rp.PopRaw<Parameters>()};
1153
1154 Core::HID::NpadIdType new_npad_id{};
1155 auto controller = GetResourceManager()->GetNpad();
1156 controller->SetNpadMode(new_npad_id, parameters.npad_id, NPad::NpadJoyDeviceType::Left,
1157 NPad::NpadJoyAssignmentMode::Single);
1158
1159 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1160 parameters.applet_resource_user_id);
1161
1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ResultSuccess);
1164}
1165
1166void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1167 IPC::RequestParser rp{ctx};
1168 struct Parameters {
1169 Core::HID::NpadIdType npad_id;
1170 INSERT_PADDING_WORDS_NOINIT(1);
1171 u64 applet_resource_user_id;
1172 NPad::NpadJoyDeviceType npad_joy_device_type;
1173 };
1174 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1175
1176 const auto parameters{rp.PopRaw<Parameters>()};
1177
1178 Core::HID::NpadIdType new_npad_id{};
1179 auto controller = GetResourceManager()->GetNpad();
1180 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1181 NPad::NpadJoyAssignmentMode::Single);
1182
1183 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1184 parameters.npad_id, parameters.applet_resource_user_id,
1185 parameters.npad_joy_device_type);
1186
1187 IPC::ResponseBuilder rb{ctx, 2};
1188 rb.Push(ResultSuccess);
1189}
1190
1191void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1192 IPC::RequestParser rp{ctx};
1193 struct Parameters {
1194 Core::HID::NpadIdType npad_id;
1195 INSERT_PADDING_WORDS_NOINIT(1);
1196 u64 applet_resource_user_id;
1197 };
1198 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1199
1200 const auto parameters{rp.PopRaw<Parameters>()};
1201
1202 Core::HID::NpadIdType new_npad_id{};
1203 auto controller = GetResourceManager()->GetNpad();
1204 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NPad::NpadJoyAssignmentMode::Dual);
1205
1206 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1207 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
1208
1209 IPC::ResponseBuilder rb{ctx, 2};
1210 rb.Push(ResultSuccess);
1211}
1212
1213void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1214 IPC::RequestParser rp{ctx};
1215 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1216 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1217 const auto applet_resource_user_id{rp.Pop<u64>()};
1218
1219 auto controller = GetResourceManager()->GetNpad();
1220 const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1221
1222 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1223 npad_id_1, npad_id_2, applet_resource_user_id);
1224
1225 IPC::ResponseBuilder rb{ctx, 2};
1226 rb.Push(result);
1227}
1228
1229void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
1230 IPC::RequestParser rp{ctx};
1231 const auto applet_resource_user_id{rp.Pop<u64>()};
1232
1233 GetResourceManager()->GetNpad()->StartLRAssignmentMode();
1234
1235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1236
1237 IPC::ResponseBuilder rb{ctx, 2};
1238 rb.Push(ResultSuccess);
1239}
1240
1241void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1242 IPC::RequestParser rp{ctx};
1243 const auto applet_resource_user_id{rp.Pop<u64>()};
1244
1245 GetResourceManager()->GetNpad()->StopLRAssignmentMode();
1246
1247 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1248
1249 IPC::ResponseBuilder rb{ctx, 2};
1250 rb.Push(ResultSuccess);
1251}
1252
1253void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1254 IPC::RequestParser rp{ctx};
1255 const auto applet_resource_user_id{rp.Pop<u64>()};
1256 const auto activation_mode{rp.PopEnum<NPad::NpadHandheldActivationMode>()};
1257
1258 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1259
1260 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1261 applet_resource_user_id, activation_mode);
1262
1263 IPC::ResponseBuilder rb{ctx, 2};
1264 rb.Push(ResultSuccess);
1265}
1266
1267void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1268 IPC::RequestParser rp{ctx};
1269 const auto applet_resource_user_id{rp.Pop<u64>()};
1270
1271 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1272
1273 IPC::ResponseBuilder rb{ctx, 4};
1274 rb.Push(ResultSuccess);
1275 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode());
1276}
1277
1278void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
1279 IPC::RequestParser rp{ctx};
1280 const auto npad_id_1{rp.PopEnum<Core::HID::NpadIdType>()};
1281 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1282 const auto applet_resource_user_id{rp.Pop<u64>()};
1283
1284 auto controller = GetResourceManager()->GetNpad();
1285 const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
1286
1287 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1288 npad_id_1, npad_id_2, applet_resource_user_id);
1289
1290 IPC::ResponseBuilder rb{ctx, 2};
1291 rb.Push(result);
1292}
1293
1294void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) {
1295 IPC::RequestParser rp{ctx};
1296 struct Parameters {
1297 Core::HID::NpadIdType npad_id;
1298 INSERT_PADDING_WORDS_NOINIT(1);
1299 u64 applet_resource_user_id;
1300 };
1301 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1302
1303 const auto parameters{rp.PopRaw<Parameters>()};
1304
1305 bool is_enabled = false;
1306 auto controller = GetResourceManager()->GetNpad();
1307 const auto result =
1308 controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1309
1310 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1311 parameters.npad_id, parameters.applet_resource_user_id);
1312
1313 IPC::ResponseBuilder rb{ctx, 3};
1314 rb.Push(result);
1315 rb.Push(is_enabled);
1316}
1317
1318void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) {
1319 IPC::RequestParser rp{ctx};
1320 struct Parameters {
1321 bool is_enabled;
1322 INSERT_PADDING_BYTES_NOINIT(3);
1323 Core::HID::NpadIdType npad_id;
1324 u64 applet_resource_user_id;
1325 };
1326 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1327
1328 const auto parameters{rp.PopRaw<Parameters>()};
1329
1330 auto controller = GetResourceManager()->GetNpad();
1331 const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled(
1332 parameters.is_enabled, parameters.npad_id);
1333
1334 LOG_DEBUG(Service_HID,
1335 "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1336 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1337
1338 IPC::ResponseBuilder rb{ctx, 2};
1339 rb.Push(result);
1340}
1341
1342void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) {
1343 IPC::RequestParser rp{ctx};
1344 struct Parameters {
1345 Core::HID::NpadIdType npad_id;
1346 INSERT_PADDING_WORDS_NOINIT(1);
1347 u64 applet_resource_user_id;
1348 NPad::NpadJoyDeviceType npad_joy_device_type;
1349 };
1350 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1351
1352 const auto parameters{rp.PopRaw<Parameters>()};
1353
1354 Core::HID::NpadIdType new_npad_id{};
1355 auto controller = GetResourceManager()->GetNpad();
1356 const auto is_reassigned =
1357 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type,
1358 NPad::NpadJoyAssignmentMode::Single);
1359
1360 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1361 parameters.npad_id, parameters.applet_resource_user_id,
1362 parameters.npad_joy_device_type);
1363
1364 IPC::ResponseBuilder rb{ctx, 4};
1365 rb.Push(ResultSuccess);
1366 rb.Push(is_reassigned);
1367 rb.PushEnum(new_npad_id);
1368}
1369
1370void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1371 IPC::RequestParser rp{ctx};
1372 struct Parameters {
1373 bool analog_stick_use_center_clamp;
1374 INSERT_PADDING_BYTES_NOINIT(7);
1375 u64 applet_resource_user_id;
1376 };
1377 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1378
1379 const auto parameters{rp.PopRaw<Parameters>()};
1380
1381 GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp(
1382 parameters.analog_stick_use_center_clamp);
1383
1384 LOG_WARNING(Service_HID,
1385 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}",
1386 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1387
1388 IPC::ResponseBuilder rb{ctx, 2};
1389 rb.Push(ResultSuccess);
1390}
1391
1392void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1393 IPC::RequestParser rp{ctx};
1394 struct Parameters {
1395 Core::HID::NpadStyleSet npad_styleset;
1396 INSERT_PADDING_WORDS_NOINIT(1);
1397 u64 applet_resource_user_id;
1398 Core::HID::NpadButton button;
1399 };
1400 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1401
1402 const auto parameters{rp.PopRaw<Parameters>()};
1403
1404 LOG_WARNING(Service_HID,
1405 "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}",
1406 parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button);
1407
1408 IPC::ResponseBuilder rb{ctx, 2};
1409 rb.Push(ResultSuccess);
1410}
1411
1412void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) {
1413 IPC::RequestParser rp{ctx};
1414 const auto applet_resource_user_id{rp.Pop<u64>()};
1415
1416 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1417 applet_resource_user_id);
1418
1419 IPC::ResponseBuilder rb{ctx, 2};
1420 rb.Push(ResultSuccess);
1421}
1422
1423void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) {
1424 IPC::RequestParser rp{ctx};
1425 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
1426 const auto controller = GetResourceManager()->GetNpad();
1427
1428 Core::HID::VibrationDeviceInfo vibration_device_info;
1429 bool check_device_index = false;
1430
1431 switch (vibration_device_handle.npad_type) {
1432 case Core::HID::NpadStyleIndex::ProController:
1433 case Core::HID::NpadStyleIndex::Handheld:
1434 case Core::HID::NpadStyleIndex::JoyconDual:
1435 case Core::HID::NpadStyleIndex::JoyconLeft:
1436 case Core::HID::NpadStyleIndex::JoyconRight:
1437 vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
1438 check_device_index = true;
1439 break;
1440 case Core::HID::NpadStyleIndex::GameCube:
1441 vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
1442 break;
1443 case Core::HID::NpadStyleIndex::N64:
1444 vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
1445 break;
1446 default:
1447 vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
1448 break;
1449 }
1450
1451 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1452 if (check_device_index) {
1453 switch (vibration_device_handle.device_index) {
1454 case Core::HID::DeviceIndex::Left:
1455 vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
1456 break;
1457 case Core::HID::DeviceIndex::Right:
1458 vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
1459 break;
1460 case Core::HID::DeviceIndex::None:
1461 default:
1462 ASSERT_MSG(false, "DeviceIndex should never be None!");
1463 break;
1464 }
1465 }
1466
1467 LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
1468 vibration_device_info.type, vibration_device_info.position);
1469
1470 const auto result = IsVibrationHandleValid(vibration_device_handle);
1471 if (result.IsError()) {
1472 IPC::ResponseBuilder rb{ctx, 2};
1473 rb.Push(result);
1474 return;
1475 }
1476
1477 IPC::ResponseBuilder rb{ctx, 4};
1478 rb.Push(ResultSuccess);
1479 rb.PushRaw(vibration_device_info);
1480}
1481
1482void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1483 IPC::RequestParser rp{ctx};
1484 struct Parameters {
1485 Core::HID::VibrationDeviceHandle vibration_device_handle;
1486 Core::HID::VibrationValue vibration_value;
1487 INSERT_PADDING_WORDS_NOINIT(1);
1488 u64 applet_resource_user_id;
1489 };
1490 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
1491
1492 const auto parameters{rp.PopRaw<Parameters>()};
1493
1494 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1495 parameters.vibration_value);
1496
1497 LOG_DEBUG(Service_HID,
1498 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1499 parameters.vibration_device_handle.npad_type,
1500 parameters.vibration_device_handle.npad_id,
1501 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1502
1503 IPC::ResponseBuilder rb{ctx, 2};
1504 rb.Push(ResultSuccess);
1505}
1506
1507void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1508 IPC::RequestParser rp{ctx};
1509 struct Parameters {
1510 Core::HID::VibrationDeviceHandle vibration_device_handle;
1511 INSERT_PADDING_WORDS_NOINIT(1);
1512 u64 applet_resource_user_id;
1513 };
1514 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1515
1516 const auto parameters{rp.PopRaw<Parameters>()};
1517
1518 LOG_DEBUG(Service_HID,
1519 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1520 parameters.vibration_device_handle.npad_type,
1521 parameters.vibration_device_handle.npad_id,
1522 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1523
1524 IPC::ResponseBuilder rb{ctx, 6};
1525 rb.Push(ResultSuccess);
1526 rb.PushRaw(
1527 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle));
1528}
1529
1530void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
1531 LOG_DEBUG(Service_HID, "called");
1532
1533 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1534 rb.Push(ResultSuccess);
1535 rb.PushIpcInterface<IActiveVibrationDeviceList>(system, GetResourceManager());
1536}
1537
1538void IHidServer::PermitVibration(HLERequestContext& ctx) {
1539 IPC::RequestParser rp{ctx};
1540 const auto can_vibrate{rp.Pop<bool>()};
1541
1542 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1543 // by converting it to a bool
1544 Settings::values.vibration_enabled.SetValue(can_vibrate);
1545
1546 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
1547
1548 IPC::ResponseBuilder rb{ctx, 2};
1549 rb.Push(ResultSuccess);
1550}
1551
1552void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) {
1553 LOG_DEBUG(Service_HID, "called");
1554
1555 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1556 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1557
1558 IPC::ResponseBuilder rb{ctx, 3};
1559 rb.Push(ResultSuccess);
1560 rb.Push(is_enabled);
1561}
1562
1563void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1564 IPC::RequestParser rp{ctx};
1565 const auto applet_resource_user_id{rp.Pop<u64>()};
1566
1567 const auto handle_data = ctx.ReadBuffer(0);
1568 const auto handle_count = ctx.GetReadBufferNumElements<Core::HID::VibrationDeviceHandle>(0);
1569 const auto vibration_data = ctx.ReadBuffer(1);
1570 const auto vibration_count = ctx.GetReadBufferNumElements<Core::HID::VibrationValue>(1);
1571
1572 auto vibration_device_handles =
1573 std::span(reinterpret_cast<const Core::HID::VibrationDeviceHandle*>(handle_data.data()),
1574 handle_count);
1575 auto vibration_values = std::span(
1576 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1577
1578 GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values);
1579
1580 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1581
1582 IPC::ResponseBuilder rb{ctx, 2};
1583 rb.Push(ResultSuccess);
1584}
1585
1586void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1587 IPC::RequestParser rp{ctx};
1588 struct Parameters {
1589 Core::HID::VibrationDeviceHandle vibration_device_handle;
1590 INSERT_PADDING_WORDS_NOINIT(1);
1591 u64 applet_resource_user_id;
1592 Core::HID::VibrationGcErmCommand gc_erm_command;
1593 };
1594 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
1595
1596 const auto parameters{rp.PopRaw<Parameters>()};
1597
1598 /**
1599 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1600 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below,
1601 * in order to differentiate between Stop and StopHard commands.
1602 * This is done to reuse the controller vibration functions made for regular controllers.
1603 */
1604 const auto vibration_value = [parameters] {
1605 switch (parameters.gc_erm_command) {
1606 case Core::HID::VibrationGcErmCommand::Stop:
1607 return Core::HID::VibrationValue{
1608 .low_amplitude = 0.0f,
1609 .low_frequency = 160.0f,
1610 .high_amplitude = 0.0f,
1611 .high_frequency = 320.0f,
1612 };
1613 case Core::HID::VibrationGcErmCommand::Start:
1614 return Core::HID::VibrationValue{
1615 .low_amplitude = 1.0f,
1616 .low_frequency = 160.0f,
1617 .high_amplitude = 1.0f,
1618 .high_frequency = 320.0f,
1619 };
1620 case Core::HID::VibrationGcErmCommand::StopHard:
1621 return Core::HID::VibrationValue{
1622 .low_amplitude = 0.0f,
1623 .low_frequency = 0.0f,
1624 .high_amplitude = 0.0f,
1625 .high_frequency = 0.0f,
1626 };
1627 default:
1628 return Core::HID::DEFAULT_VIBRATION_VALUE;
1629 }
1630 }();
1631
1632 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle,
1633 vibration_value);
1634
1635 LOG_DEBUG(Service_HID,
1636 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
1637 "gc_erm_command={}",
1638 parameters.vibration_device_handle.npad_type,
1639 parameters.vibration_device_handle.npad_id,
1640 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id,
1641 parameters.gc_erm_command);
1642
1643 IPC::ResponseBuilder rb{ctx, 2};
1644 rb.Push(ResultSuccess);
1645}
1646
1647void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1648 IPC::RequestParser rp{ctx};
1649 struct Parameters {
1650 Core::HID::VibrationDeviceHandle vibration_device_handle;
1651 INSERT_PADDING_WORDS_NOINIT(1);
1652 u64 applet_resource_user_id;
1653 };
1654
1655 const auto parameters{rp.PopRaw<Parameters>()};
1656
1657 const auto last_vibration =
1658 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle);
1659
1660 const auto gc_erm_command = [last_vibration] {
1661 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
1662 return Core::HID::VibrationGcErmCommand::Start;
1663 }
1664
1665 /**
1666 * Note: This uses yuzu-specific behavior such that the StopHard command produces
1667 * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function
1668 * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands.
1669 * This is done to reuse the controller vibration functions made for regular controllers.
1670 */
1671 if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) {
1672 return Core::HID::VibrationGcErmCommand::StopHard;
1673 }
1674
1675 return Core::HID::VibrationGcErmCommand::Stop;
1676 }();
1677
1678 LOG_DEBUG(Service_HID,
1679 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1680 parameters.vibration_device_handle.npad_type,
1681 parameters.vibration_device_handle.npad_id,
1682 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1683
1684 IPC::ResponseBuilder rb{ctx, 4};
1685 rb.Push(ResultSuccess);
1686 rb.PushEnum(gc_erm_command);
1687}
1688
1689void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) {
1690 IPC::RequestParser rp{ctx};
1691 const auto applet_resource_user_id{rp.Pop<u64>()};
1692
1693 GetResourceManager()->GetNpad()->SetPermitVibrationSession(true);
1694
1695 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1696
1697 IPC::ResponseBuilder rb{ctx, 2};
1698 rb.Push(ResultSuccess);
1699}
1700
1701void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) {
1702 GetResourceManager()->GetNpad()->SetPermitVibrationSession(false);
1703
1704 LOG_DEBUG(Service_HID, "called");
1705
1706 IPC::ResponseBuilder rb{ctx, 2};
1707 rb.Push(ResultSuccess);
1708}
1709
1710void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1711 IPC::RequestParser rp{ctx};
1712 struct Parameters {
1713 Core::HID::VibrationDeviceHandle vibration_device_handle;
1714 INSERT_PADDING_WORDS_NOINIT(1);
1715 u64 applet_resource_user_id;
1716 };
1717 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1718
1719 const auto parameters{rp.PopRaw<Parameters>()};
1720
1721 LOG_DEBUG(Service_HID,
1722 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
1723 parameters.vibration_device_handle.npad_type,
1724 parameters.vibration_device_handle.npad_id,
1725 parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
1726
1727 IPC::ResponseBuilder rb{ctx, 3};
1728 rb.Push(ResultSuccess);
1729 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
1730 parameters.vibration_device_handle));
1731}
1732
1733void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1734 IPC::RequestParser rp{ctx};
1735 const auto applet_resource_user_id{rp.Pop<u64>()};
1736
1737 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1738
1739 Result result = ResultSuccess;
1740 auto console_sixaxis = GetResourceManager()->GetConsoleSixAxis();
1741
1742 if (!firmware_settings->IsDeviceManaged()) {
1743 result = console_sixaxis->Activate();
1744 }
1745
1746 if (result.IsSuccess()) {
1747 result = console_sixaxis->Activate(applet_resource_user_id);
1748 }
1749
1750 IPC::ResponseBuilder rb{ctx, 2};
1751 rb.Push(result);
1752}
1753
1754void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
1755 IPC::RequestParser rp{ctx};
1756 struct Parameters {
1757 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1758 INSERT_PADDING_WORDS_NOINIT(1);
1759 u64 applet_resource_user_id;
1760 };
1761 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1762
1763 const auto parameters{rp.PopRaw<Parameters>()};
1764
1765 LOG_WARNING(Service_HID,
1766 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1767 parameters.console_sixaxis_handle.unknown_1,
1768 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1769
1770 IPC::ResponseBuilder rb{ctx, 2};
1771 rb.Push(ResultSuccess);
1772}
1773
1774void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) {
1775 IPC::RequestParser rp{ctx};
1776 struct Parameters {
1777 Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle;
1778 INSERT_PADDING_WORDS_NOINIT(1);
1779 u64 applet_resource_user_id;
1780 };
1781 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1782
1783 const auto parameters{rp.PopRaw<Parameters>()};
1784
1785 LOG_WARNING(Service_HID,
1786 "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
1787 parameters.console_sixaxis_handle.unknown_1,
1788 parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id);
1789
1790 IPC::ResponseBuilder rb{ctx, 2};
1791 rb.Push(ResultSuccess);
1792}
1793
1794void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1795 IPC::RequestParser rp{ctx};
1796 const auto applet_resource_user_id{rp.Pop<u64>()};
1797
1798 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1799
1800 Result result = ResultSuccess;
1801 auto seven_sixaxis = GetResourceManager()->GetSevenSixAxis();
1802
1803 if (!firmware_settings->IsDeviceManaged()) {
1804 result = seven_sixaxis->Activate();
1805 }
1806
1807 if (result.IsSuccess()) {
1808 seven_sixaxis->Activate(applet_resource_user_id);
1809 }
1810
1811 IPC::ResponseBuilder rb{ctx, 2};
1812 rb.Push(ResultSuccess);
1813}
1814
1815void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) {
1816 IPC::RequestParser rp{ctx};
1817 const auto applet_resource_user_id{rp.Pop<u64>()};
1818
1819 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1820 applet_resource_user_id);
1821
1822 IPC::ResponseBuilder rb{ctx, 2};
1823 rb.Push(ResultSuccess);
1824}
1825
1826void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) {
1827 IPC::RequestParser rp{ctx};
1828 const auto applet_resource_user_id{rp.Pop<u64>()};
1829
1830 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1831 applet_resource_user_id);
1832
1833 IPC::ResponseBuilder rb{ctx, 2};
1834 rb.Push(ResultSuccess);
1835}
1836
1837void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1838 IPC::RequestParser rp{ctx};
1839 const auto applet_resource_user_id{rp.Pop<u64>()};
1840 const auto t_mem_1_size{rp.Pop<u64>()};
1841 const auto t_mem_2_size{rp.Pop<u64>()};
1842 const auto t_mem_1_handle{ctx.GetCopyHandle(0)};
1843 const auto t_mem_2_handle{ctx.GetCopyHandle(1)};
1844
1845 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1846 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1847
1848 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1849 t_mem_1_handle);
1850
1851 if (t_mem_1.IsNull()) {
1852 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
1853 IPC::ResponseBuilder rb{ctx, 2};
1854 rb.Push(ResultUnknown);
1855 return;
1856 }
1857
1858 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
1859 t_mem_2_handle);
1860
1861 if (t_mem_2.IsNull()) {
1862 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
1863 IPC::ResponseBuilder rb{ctx, 2};
1864 rb.Push(ResultUnknown);
1865 return;
1866 }
1867
1868 ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size");
1869 ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size");
1870
1871 // Activate console six axis controller
1872 GetResourceManager()->GetConsoleSixAxis()->Activate();
1873 GetResourceManager()->GetSevenSixAxis()->Activate();
1874
1875 GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
1876
1877 LOG_WARNING(Service_HID,
1878 "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, "
1879 "applet_resource_user_id={}",
1880 t_mem_1_handle, t_mem_2_handle, applet_resource_user_id);
1881
1882 IPC::ResponseBuilder rb{ctx, 2};
1883 rb.Push(ResultSuccess);
1884}
1885
1886void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) {
1887 IPC::RequestParser rp{ctx};
1888 const auto applet_resource_user_id{rp.Pop<u64>()};
1889
1890 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
1891 applet_resource_user_id);
1892
1893 IPC::ResponseBuilder rb{ctx, 2};
1894 rb.Push(ResultSuccess);
1895}
1896
1897void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) {
1898 IPC::RequestParser rp{ctx};
1899 const auto applet_resource_user_id{rp.Pop<u64>()};
1900
1901 GetResourceManager()->GetSevenSixAxis()->ResetTimestamp();
1902
1903 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1904
1905 IPC::ResponseBuilder rb{ctx, 2};
1906 rb.Push(ResultSuccess);
1907}
1908
1909void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
1910 IPC::RequestParser rp{ctx};
1911
1912 LOG_WARNING(Service_HID, "(STUBBED) called");
1913
1914 IPC::ResponseBuilder rb{ctx, 3};
1915 rb.Push(ResultSuccess);
1916 rb.Push(false);
1917}
1918
1919void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) {
1920 IPC::RequestParser rp{ctx};
1921 struct Parameters {
1922 Core::HID::NpadIdType npad_id;
1923 INSERT_PADDING_WORDS_NOINIT(1);
1924 u64 applet_resource_user_id;
1925 };
1926 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
1927
1928 const auto parameters{rp.PopRaw<Parameters>()};
1929
1930 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1931 parameters.npad_id, parameters.applet_resource_user_id);
1932
1933 Palma::PalmaConnectionHandle handle;
1934 auto controller = GetResourceManager()->GetPalma();
1935 const auto result = controller->GetPalmaConnectionHandle(parameters.npad_id, handle);
1936
1937 IPC::ResponseBuilder rb{ctx, 4};
1938 rb.Push(result);
1939 rb.PushRaw(handle);
1940}
1941
1942void IHidServer::InitializePalma(HLERequestContext& ctx) {
1943 IPC::RequestParser rp{ctx};
1944 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1945
1946 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1947
1948 auto controller = GetResourceManager()->GetPalma();
1949 const auto result = controller->InitializePalma(connection_handle);
1950
1951 IPC::ResponseBuilder rb{ctx, 2};
1952 rb.Push(result);
1953}
1954
1955void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) {
1956 IPC::RequestParser rp{ctx};
1957 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1958
1959 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1960
1961 auto controller = GetResourceManager()->GetPalma();
1962
1963 IPC::ResponseBuilder rb{ctx, 2, 1};
1964 rb.Push(ResultSuccess);
1965 rb.PushCopyObjects(controller->AcquirePalmaOperationCompleteEvent(connection_handle));
1966}
1967
1968void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) {
1969 IPC::RequestParser rp{ctx};
1970 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1971
1972 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
1973
1974 Palma::PalmaOperationType operation_type;
1975 Palma::PalmaOperationData data;
1976 auto controller = GetResourceManager()->GetPalma();
1977 const auto result = controller->GetPalmaOperationInfo(connection_handle, operation_type, data);
1978
1979 if (result.IsError()) {
1980 IPC::ResponseBuilder rb{ctx, 2};
1981 rb.Push(result);
1982 }
1983
1984 ctx.WriteBuffer(data);
1985 IPC::ResponseBuilder rb{ctx, 4};
1986 rb.Push(result);
1987 rb.Push(static_cast<u64>(operation_type));
1988}
1989
1990void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) {
1991 IPC::RequestParser rp{ctx};
1992 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
1993 const auto palma_activity{rp.Pop<u64>()};
1994
1995 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
1996 connection_handle.npad_id, palma_activity);
1997
1998 auto controller = GetResourceManager()->GetPalma();
1999 const auto result = controller->PlayPalmaActivity(connection_handle, palma_activity);
2000
2001 IPC::ResponseBuilder rb{ctx, 2};
2002 rb.Push(result);
2003}
2004
2005void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) {
2006 IPC::RequestParser rp{ctx};
2007 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2008 const auto fr_mode{rp.PopEnum<Palma::PalmaFrModeType>()};
2009
2010 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
2011 connection_handle.npad_id, fr_mode);
2012
2013 auto controller = GetResourceManager()->GetPalma();
2014 const auto result = controller->SetPalmaFrModeType(connection_handle, fr_mode);
2015
2016 IPC::ResponseBuilder rb{ctx, 2};
2017 rb.Push(result);
2018}
2019
2020void IHidServer::ReadPalmaStep(HLERequestContext& ctx) {
2021 IPC::RequestParser rp{ctx};
2022 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2023
2024 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2025
2026 auto controller = GetResourceManager()->GetPalma();
2027 const auto result = controller->ReadPalmaStep(connection_handle);
2028
2029 IPC::ResponseBuilder rb{ctx, 2};
2030 rb.Push(result);
2031}
2032
2033void IHidServer::EnablePalmaStep(HLERequestContext& ctx) {
2034 IPC::RequestParser rp{ctx};
2035 struct Parameters {
2036 bool is_enabled;
2037 INSERT_PADDING_WORDS_NOINIT(1);
2038 Palma::PalmaConnectionHandle connection_handle;
2039 };
2040 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2041
2042 const auto parameters{rp.PopRaw<Parameters>()};
2043
2044 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
2045 parameters.connection_handle.npad_id, parameters.is_enabled);
2046
2047 auto controller = GetResourceManager()->GetPalma();
2048 const auto result =
2049 controller->EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
2050
2051 IPC::ResponseBuilder rb{ctx, 2};
2052 rb.Push(result);
2053}
2054
2055void IHidServer::ResetPalmaStep(HLERequestContext& ctx) {
2056 IPC::RequestParser rp{ctx};
2057 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2058
2059 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2060
2061 auto controller = GetResourceManager()->GetPalma();
2062 const auto result = controller->ResetPalmaStep(connection_handle);
2063
2064 IPC::ResponseBuilder rb{ctx, 2};
2065 rb.Push(result);
2066}
2067
2068void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) {
2069 LOG_WARNING(Service_HID, "(STUBBED) called");
2070
2071 IPC::ResponseBuilder rb{ctx, 2};
2072 rb.Push(ResultSuccess);
2073}
2074
2075void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) {
2076 LOG_WARNING(Service_HID, "(STUBBED) called");
2077
2078 IPC::ResponseBuilder rb{ctx, 2};
2079 rb.Push(ResultSuccess);
2080}
2081
2082void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) {
2083 IPC::RequestParser rp{ctx};
2084 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2085
2086 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2087
2088 GetResourceManager()->GetPalma()->ReadPalmaUniqueCode(connection_handle);
2089
2090 IPC::ResponseBuilder rb{ctx, 2};
2091 rb.Push(ResultSuccess);
2092}
2093
2094void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) {
2095 IPC::RequestParser rp{ctx};
2096 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2097
2098 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2099
2100 GetResourceManager()->GetPalma()->SetPalmaUniqueCodeInvalid(connection_handle);
2101
2102 IPC::ResponseBuilder rb{ctx, 2};
2103 rb.Push(ResultSuccess);
2104}
2105
2106void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) {
2107 LOG_CRITICAL(Service_HID, "(STUBBED) called");
2108
2109 IPC::ResponseBuilder rb{ctx, 2};
2110 rb.Push(ResultSuccess);
2111}
2112
2113void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) {
2114 IPC::RequestParser rp{ctx};
2115 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2116 const auto unknown{rp.Pop<u64>()};
2117
2118 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
2119
2120 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
2121 connection_handle.npad_id, unknown);
2122
2123 GetResourceManager()->GetPalma()->WritePalmaRgbLedPatternEntry(connection_handle, unknown);
2124
2125 IPC::ResponseBuilder rb{ctx, 2};
2126 rb.Push(ResultSuccess);
2127}
2128
2129void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2130 IPC::RequestParser rp{ctx};
2131 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2132 const auto wave_set{rp.PopEnum<Palma::PalmaWaveSet>()};
2133 const auto unknown{rp.Pop<u64>()};
2134 const auto t_mem_size{rp.Pop<u64>()};
2135 const auto t_mem_handle{ctx.GetCopyHandle(0)};
2136 const auto size{rp.Pop<u64>()};
2137
2138 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2139
2140 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
2141 t_mem_handle);
2142
2143 if (t_mem.IsNull()) {
2144 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
2145 IPC::ResponseBuilder rb{ctx, 2};
2146 rb.Push(ResultUnknown);
2147 return;
2148 }
2149
2150 ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
2151
2152 LOG_WARNING(Service_HID,
2153 "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, "
2154 "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
2155 connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
2156
2157 GetResourceManager()->GetPalma()->WritePalmaWaveEntry(connection_handle, wave_set,
2158 t_mem->GetSourceAddress(), t_mem_size);
2159
2160 IPC::ResponseBuilder rb{ctx, 2};
2161 rb.Push(ResultSuccess);
2162}
2163
2164void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2165 IPC::RequestParser rp{ctx};
2166 struct Parameters {
2167 s32 database_id_version;
2168 INSERT_PADDING_WORDS_NOINIT(1);
2169 Palma::PalmaConnectionHandle connection_handle;
2170 };
2171 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2172
2173 const auto parameters{rp.PopRaw<Parameters>()};
2174
2175 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
2176 parameters.connection_handle.npad_id, parameters.database_id_version);
2177
2178 GetResourceManager()->GetPalma()->SetPalmaDataBaseIdentificationVersion(
2179 parameters.connection_handle, parameters.database_id_version);
2180
2181 IPC::ResponseBuilder rb{ctx, 2};
2182 rb.Push(ResultSuccess);
2183}
2184
2185void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) {
2186 IPC::RequestParser rp{ctx};
2187 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2188
2189 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2190
2191 GetResourceManager()->GetPalma()->GetPalmaDataBaseIdentificationVersion(connection_handle);
2192
2193 IPC::ResponseBuilder rb{ctx, 2};
2194 rb.Push(ResultSuccess);
2195}
2196
2197void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) {
2198 LOG_WARNING(Service_HID, "(STUBBED) called");
2199
2200 IPC::ResponseBuilder rb{ctx, 2};
2201 rb.Push(ResultSuccess);
2202}
2203
2204void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) {
2205 IPC::RequestParser rp{ctx};
2206 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2207
2208 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2209
2210 const auto result =
2211 GetResourceManager()->GetPalma()->GetPalmaOperationResult(connection_handle);
2212
2213 IPC::ResponseBuilder rb{ctx, 2};
2214 rb.Push(result);
2215}
2216
2217void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) {
2218 LOG_WARNING(Service_HID, "(STUBBED) called");
2219
2220 IPC::ResponseBuilder rb{ctx, 2};
2221 rb.Push(ResultSuccess);
2222}
2223
2224void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) {
2225 LOG_WARNING(Service_HID, "(STUBBED) called");
2226
2227 IPC::ResponseBuilder rb{ctx, 2};
2228 rb.Push(ResultSuccess);
2229}
2230
2231void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) {
2232 IPC::RequestParser rp{ctx};
2233 struct Parameters {
2234 bool is_palma_all_connectable;
2235 INSERT_PADDING_BYTES_NOINIT(7);
2236 u64 applet_resource_user_id;
2237 };
2238 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2239
2240 const auto parameters{rp.PopRaw<Parameters>()};
2241
2242 LOG_WARNING(Service_HID,
2243 "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
2244 parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
2245
2246 GetResourceManager()->GetPalma()->SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
2247
2248 IPC::ResponseBuilder rb{ctx, 2};
2249 rb.Push(ResultSuccess);
2250}
2251
2252void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) {
2253 LOG_WARNING(Service_HID, "(STUBBED) called");
2254
2255 IPC::ResponseBuilder rb{ctx, 2};
2256 rb.Push(ResultSuccess);
2257}
2258
2259void IHidServer::PairPalma(HLERequestContext& ctx) {
2260 IPC::RequestParser rp{ctx};
2261 const auto connection_handle{rp.PopRaw<Palma::PalmaConnectionHandle>()};
2262
2263 LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
2264
2265 GetResourceManager()->GetPalma()->PairPalma(connection_handle);
2266
2267 IPC::ResponseBuilder rb{ctx, 2};
2268 rb.Push(ResultSuccess);
2269}
2270
2271void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) {
2272 IPC::RequestParser rp{ctx};
2273 const auto palma_boost_mode{rp.Pop<bool>()};
2274
2275 LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
2276
2277 GetResourceManager()->GetPalma()->SetPalmaBoostMode(palma_boost_mode);
2278
2279 IPC::ResponseBuilder rb{ctx, 2};
2280 rb.Push(ResultSuccess);
2281}
2282
2283void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) {
2284 LOG_WARNING(Service_HID, "(STUBBED) called");
2285
2286 IPC::ResponseBuilder rb{ctx, 2};
2287 rb.Push(ResultSuccess);
2288}
2289
2290void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) {
2291 LOG_WARNING(Service_HID, "(STUBBED) called");
2292
2293 IPC::ResponseBuilder rb{ctx, 2};
2294 rb.Push(ResultSuccess);
2295}
2296
2297void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) {
2298 LOG_WARNING(Service_HID, "(STUBBED) called");
2299
2300 IPC::ResponseBuilder rb{ctx, 2};
2301 rb.Push(ResultSuccess);
2302}
2303
2304void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) {
2305 LOG_WARNING(Service_HID, "(STUBBED) called");
2306
2307 IPC::ResponseBuilder rb{ctx, 2};
2308 rb.Push(ResultSuccess);
2309}
2310
2311void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2312 IPC::RequestParser rp{ctx};
2313 const auto applet_resource_user_id{rp.Pop<u64>()};
2314 const auto communication_mode{rp.PopEnum<NPad::NpadCommunicationMode>()};
2315
2316 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode);
2317
2318 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}",
2319 applet_resource_user_id, communication_mode);
2320
2321 IPC::ResponseBuilder rb{ctx, 2};
2322 rb.Push(ResultSuccess);
2323}
2324
2325void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2326 IPC::RequestParser rp{ctx};
2327
2328 LOG_WARNING(Service_HID, "(STUBBED) called");
2329
2330 IPC::ResponseBuilder rb{ctx, 4};
2331 rb.Push(ResultSuccess);
2332 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode());
2333}
2334
2335void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
2336 IPC::RequestParser rp{ctx};
2337 const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()};
2338 const auto applet_resource_user_id{rp.Pop<u64>()};
2339
2340 LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}",
2341 touchscreen_mode.mode, applet_resource_user_id);
2342
2343 IPC::ResponseBuilder rb{ctx, 2};
2344 rb.Push(ResultSuccess);
2345}
2346
2347void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) {
2348 IPC::RequestParser rp{ctx};
2349 struct Parameters {
2350 s32 unknown;
2351 INSERT_PADDING_WORDS_NOINIT(1);
2352 u64 applet_resource_user_id;
2353 };
2354 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
2355
2356 const auto parameters{rp.PopRaw<Parameters>()};
2357
2358 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
2359 parameters.unknown, parameters.applet_resource_user_id);
2360
2361 IPC::ResponseBuilder rb{ctx, 3};
2362 rb.Push(ResultSuccess);
2363 rb.Push(false);
2364}
2365
2366std::shared_ptr<ResourceManager> IHidServer::GetResourceManager() {
2367 resource_manager->Initialize();
2368 return resource_manager;
2369}
2370
2371} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
new file mode 100644
index 000000000..eb2e8e7f4
--- /dev/null
+++ b/src/core/hle/service/hid/hid_server.h
@@ -0,0 +1,149 @@
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/hle/service/service.h"
7
8namespace Core {
9class System;
10}
11
12namespace Service::HID {
13class ResourceManager;
14class HidFirmwareSettings;
15
16class IHidServer final : public ServiceFramework<IHidServer> {
17public:
18 explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings);
20 ~IHidServer() override;
21
22 std::shared_ptr<ResourceManager> GetResourceManager();
23
24private:
25 void CreateAppletResource(HLERequestContext& ctx);
26 void ActivateDebugPad(HLERequestContext& ctx);
27 void ActivateTouchScreen(HLERequestContext& ctx);
28 void ActivateMouse(HLERequestContext& ctx);
29 void ActivateKeyboard(HLERequestContext& ctx);
30 void SendKeyboardLockKeyEvent(HLERequestContext& ctx);
31 void AcquireXpadIdEventHandle(HLERequestContext& ctx);
32 void ReleaseXpadIdEventHandle(HLERequestContext& ctx);
33 void ActivateXpad(HLERequestContext& ctx);
34 void GetXpadIds(HLERequestContext& ctx);
35 void ActivateJoyXpad(HLERequestContext& ctx);
36 void GetJoyXpadLifoHandle(HLERequestContext& ctx);
37 void GetJoyXpadIds(HLERequestContext& ctx);
38 void ActivateSixAxisSensor(HLERequestContext& ctx);
39 void DeactivateSixAxisSensor(HLERequestContext& ctx);
40 void GetSixAxisSensorLifoHandle(HLERequestContext& ctx);
41 void ActivateJoySixAxisSensor(HLERequestContext& ctx);
42 void DeactivateJoySixAxisSensor(HLERequestContext& ctx);
43 void GetJoySixAxisSensorLifoHandle(HLERequestContext& ctx);
44 void StartSixAxisSensor(HLERequestContext& ctx);
45 void StopSixAxisSensor(HLERequestContext& ctx);
46 void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx);
47 void EnableSixAxisSensorFusion(HLERequestContext& ctx);
48 void SetSixAxisSensorFusionParameters(HLERequestContext& ctx);
49 void GetSixAxisSensorFusionParameters(HLERequestContext& ctx);
50 void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx);
51 void SetGyroscopeZeroDriftMode(HLERequestContext& ctx);
52 void GetGyroscopeZeroDriftMode(HLERequestContext& ctx);
53 void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx);
54 void IsSixAxisSensorAtRest(HLERequestContext& ctx);
55 void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx);
56 void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx);
57 void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx);
58 void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx);
59 void GetSixAxisSensorIcInformation(HLERequestContext& ctx);
60 void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx);
61 void ActivateGesture(HLERequestContext& ctx);
62 void SetSupportedNpadStyleSet(HLERequestContext& ctx);
63 void GetSupportedNpadStyleSet(HLERequestContext& ctx);
64 void SetSupportedNpadIdType(HLERequestContext& ctx);
65 void ActivateNpad(HLERequestContext& ctx);
66 void DeactivateNpad(HLERequestContext& ctx);
67 void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx);
68 void DisconnectNpad(HLERequestContext& ctx);
69 void GetPlayerLedPattern(HLERequestContext& ctx);
70 void ActivateNpadWithRevision(HLERequestContext& ctx);
71 void SetNpadJoyHoldType(HLERequestContext& ctx);
72 void GetNpadJoyHoldType(HLERequestContext& ctx);
73 void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx);
74 void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx);
75 void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx);
76 void MergeSingleJoyAsDualJoy(HLERequestContext& ctx);
77 void StartLrAssignmentMode(HLERequestContext& ctx);
78 void StopLrAssignmentMode(HLERequestContext& ctx);
79 void SetNpadHandheldActivationMode(HLERequestContext& ctx);
80 void GetNpadHandheldActivationMode(HLERequestContext& ctx);
81 void SwapNpadAssignment(HLERequestContext& ctx);
82 void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx);
83 void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx);
84 void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx);
85 void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx);
86 void SetNpadCaptureButtonAssignment(HLERequestContext& ctx);
87 void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx);
88 void GetVibrationDeviceInfo(HLERequestContext& ctx);
89 void SendVibrationValue(HLERequestContext& ctx);
90 void GetActualVibrationValue(HLERequestContext& ctx);
91 void CreateActiveVibrationDeviceList(HLERequestContext& ctx);
92 void PermitVibration(HLERequestContext& ctx);
93 void IsVibrationPermitted(HLERequestContext& ctx);
94 void SendVibrationValues(HLERequestContext& ctx);
95 void SendVibrationGcErmCommand(HLERequestContext& ctx);
96 void GetActualVibrationGcErmCommand(HLERequestContext& ctx);
97 void BeginPermitVibrationSession(HLERequestContext& ctx);
98 void EndPermitVibrationSession(HLERequestContext& ctx);
99 void IsVibrationDeviceMounted(HLERequestContext& ctx);
100 void ActivateConsoleSixAxisSensor(HLERequestContext& ctx);
101 void StartConsoleSixAxisSensor(HLERequestContext& ctx);
102 void StopConsoleSixAxisSensor(HLERequestContext& ctx);
103 void ActivateSevenSixAxisSensor(HLERequestContext& ctx);
104 void StartSevenSixAxisSensor(HLERequestContext& ctx);
105 void StopSevenSixAxisSensor(HLERequestContext& ctx);
106 void InitializeSevenSixAxisSensor(HLERequestContext& ctx);
107 void FinalizeSevenSixAxisSensor(HLERequestContext& ctx);
108 void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx);
109 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
110 void GetPalmaConnectionHandle(HLERequestContext& ctx);
111 void InitializePalma(HLERequestContext& ctx);
112 void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx);
113 void GetPalmaOperationInfo(HLERequestContext& ctx);
114 void PlayPalmaActivity(HLERequestContext& ctx);
115 void SetPalmaFrModeType(HLERequestContext& ctx);
116 void ReadPalmaStep(HLERequestContext& ctx);
117 void EnablePalmaStep(HLERequestContext& ctx);
118 void ResetPalmaStep(HLERequestContext& ctx);
119 void ReadPalmaApplicationSection(HLERequestContext& ctx);
120 void WritePalmaApplicationSection(HLERequestContext& ctx);
121 void ReadPalmaUniqueCode(HLERequestContext& ctx);
122 void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx);
123 void WritePalmaActivityEntry(HLERequestContext& ctx);
124 void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx);
125 void WritePalmaWaveEntry(HLERequestContext& ctx);
126 void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
127 void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx);
128 void SuspendPalmaFeature(HLERequestContext& ctx);
129 void GetPalmaOperationResult(HLERequestContext& ctx);
130 void ReadPalmaPlayLog(HLERequestContext& ctx);
131 void ResetPalmaPlayLog(HLERequestContext& ctx);
132 void SetIsPalmaAllConnectable(HLERequestContext& ctx);
133 void SetIsPalmaPairedConnectable(HLERequestContext& ctx);
134 void PairPalma(HLERequestContext& ctx);
135 void SetPalmaBoostMode(HLERequestContext& ctx);
136 void CancelWritePalmaWaveEntry(HLERequestContext& ctx);
137 void EnablePalmaBoostMode(HLERequestContext& ctx);
138 void GetPalmaBluetoothAddress(HLERequestContext& ctx);
139 void SetDisallowedPalmaConnection(HLERequestContext& ctx);
140 void SetNpadCommunicationMode(HLERequestContext& ctx);
141 void GetNpadCommunicationMode(HLERequestContext& ctx);
142 void SetTouchScreenConfiguration(HLERequestContext& ctx);
143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
144
145 std::shared_ptr<ResourceManager> resource_manager;
146 std::shared_ptr<HidFirmwareSettings> firmware_settings;
147};
148
149} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
new file mode 100644
index 000000000..b56d0347a
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -0,0 +1,539 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hid/hid_core.h"
5#include "core/hle/service/hid/controllers/npad.h"
6#include "core/hle/service/hid/controllers/touchscreen.h"
7#include "core/hle/service/hid/errors.h"
8#include "core/hle/service/hid/hid_system_server.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12namespace Service::HID {
13
14IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource)
15 : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name},
16 resource_manager{resource} {
17 // clang-format off
18 static const FunctionInfo functions[] = {
19 {31, nullptr, "SendKeyboardLockKeyEvent"},
20 {101, nullptr, "AcquireHomeButtonEventHandle"},
21 {111, nullptr, "ActivateHomeButton"},
22 {121, nullptr, "AcquireSleepButtonEventHandle"},
23 {131, nullptr, "ActivateSleepButton"},
24 {141, nullptr, "AcquireCaptureButtonEventHandle"},
25 {151, nullptr, "ActivateCaptureButton"},
26 {161, nullptr, "GetPlatformConfig"},
27 {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"},
28 {211, nullptr, "GetNpadsWithNfc"},
29 {212, nullptr, "AcquireNfcActivateEventHandle"},
30 {213, nullptr, "ActivateNfc"},
31 {214, nullptr, "GetXcdHandleForNpadWithNfc"},
32 {215, nullptr, "IsNfcActivated"},
33 {230, nullptr, "AcquireIrSensorEventHandle"},
34 {231, nullptr, "ActivateIrSensor"},
35 {232, nullptr, "GetIrSensorState"},
36 {233, nullptr, "GetXcdHandleForNpadWithIrSensor"},
37 {301, nullptr, "ActivateNpadSystem"},
38 {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"},
39 {304, &IHidSystemServer::EnableAssigningSingleOnSlSrPress, "EnableAssigningSingleOnSlSrPress"},
40 {305, &IHidSystemServer::DisableAssigningSingleOnSlSrPress, "DisableAssigningSingleOnSlSrPress"},
41 {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"},
42 {307, nullptr, "GetNpadSystemExtStyle"},
43 {308, &IHidSystemServer::ApplyNpadSystemCommonPolicyFull, "ApplyNpadSystemCommonPolicyFull"},
44 {309, &IHidSystemServer::GetNpadFullKeyGripColor, "GetNpadFullKeyGripColor"},
45 {310, &IHidSystemServer::GetMaskedSupportedNpadStyleSet, "GetMaskedSupportedNpadStyleSet"},
46 {311, nullptr, "SetNpadPlayerLedBlinkingDevice"},
47 {312, &IHidSystemServer::SetSupportedNpadStyleSetAll, "SetSupportedNpadStyleSetAll"},
48 {313, nullptr, "GetNpadCaptureButtonAssignment"},
49 {314, nullptr, "GetAppletFooterUiType"},
50 {315, &IHidSystemServer::GetAppletDetailedUiType, "GetAppletDetailedUiType"},
51 {316, &IHidSystemServer::GetNpadInterfaceType, "GetNpadInterfaceType"},
52 {317, &IHidSystemServer::GetNpadLeftRightInterfaceType, "GetNpadLeftRightInterfaceType"},
53 {318, &IHidSystemServer::HasBattery, "HasBattery"},
54 {319, &IHidSystemServer::HasLeftRightBattery, "HasLeftRightBattery"},
55 {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"},
56 {322, &IHidSystemServer::GetIrSensorState, "GetIrSensorState"},
57 {323, nullptr, "GetXcdHandleForNpadWithIrSensor"},
58 {324, nullptr, "GetUniquePadButtonSet"},
59 {325, nullptr, "GetUniquePadColor"},
60 {326, nullptr, "GetUniquePadAppletDetailedUiType"},
61 {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
62 {328, nullptr, "AttachAbstractedPadToNpad"},
63 {329, nullptr, "DetachAbstractedPadAll"},
64 {330, nullptr, "CheckAbstractedPadConnection"},
65 {500, nullptr, "SetAppletResourceUserId"},
66 {501, nullptr, "RegisterAppletResourceUserId"},
67 {502, nullptr, "UnregisterAppletResourceUserId"},
68 {503, nullptr, "EnableAppletToGetInput"},
69 {504, nullptr, "SetAruidValidForVibration"},
70 {505, nullptr, "EnableAppletToGetSixAxisSensor"},
71 {506, nullptr, "EnableAppletToGetPadInput"},
72 {507, nullptr, "EnableAppletToGetTouchScreen"},
73 {510, nullptr, "SetVibrationMasterVolume"},
74 {511, nullptr, "GetVibrationMasterVolume"},
75 {512, nullptr, "BeginPermitVibrationSession"},
76 {513, nullptr, "EndPermitVibrationSession"},
77 {514, nullptr, "Unknown514"},
78 {520, nullptr, "EnableHandheldHids"},
79 {521, nullptr, "DisableHandheldHids"},
80 {522, nullptr, "SetJoyConRailEnabled"},
81 {523, nullptr, "IsJoyConRailEnabled"},
82 {524, nullptr, "IsHandheldHidsEnabled"},
83 {525, nullptr, "IsJoyConAttachedOnAllRail"},
84 {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"},
85 {541, nullptr, "GetPlayReportControllerUsages"},
86 {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"},
87 {543, nullptr, "GetRegisteredDevicesOld"},
88 {544, &IHidSystemServer::AcquireConnectionTriggerTimeoutEvent, "AcquireConnectionTriggerTimeoutEvent"},
89 {545, nullptr, "SendConnectionTrigger"},
90 {546, &IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport, "AcquireDeviceRegisteredEventForControllerSupport"},
91 {547, nullptr, "GetAllowedBluetoothLinksCount"},
92 {548, &IHidSystemServer::GetRegisteredDevices, "GetRegisteredDevices"},
93 {549, nullptr, "GetConnectableRegisteredDevices"},
94 {700, nullptr, "ActivateUniquePad"},
95 {702, &IHidSystemServer::AcquireUniquePadConnectionEventHandle, "AcquireUniquePadConnectionEventHandle"},
96 {703, &IHidSystemServer::GetUniquePadIds, "GetUniquePadIds"},
97 {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"},
98 {800, nullptr, "ListSixAxisSensorHandles"},
99 {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"},
100 {802, nullptr, "ResetSixAxisSensorCalibrationValues"},
101 {803, nullptr, "StartSixAxisSensorUserCalibration"},
102 {804, nullptr, "CancelSixAxisSensorUserCalibration"},
103 {805, nullptr, "GetUniquePadBluetoothAddress"},
104 {806, nullptr, "DisconnectUniquePad"},
105 {807, nullptr, "GetUniquePadType"},
106 {808, nullptr, "GetUniquePadInterface"},
107 {809, nullptr, "GetUniquePadSerialNumber"},
108 {810, nullptr, "GetUniquePadControllerNumber"},
109 {811, nullptr, "GetSixAxisSensorUserCalibrationStage"},
110 {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"},
111 {821, nullptr, "StartAnalogStickManualCalibration"},
112 {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"},
113 {823, nullptr, "CancelAnalogStickManualCalibration"},
114 {824, nullptr, "ResetAnalogStickManualCalibration"},
115 {825, nullptr, "GetAnalogStickState"},
116 {826, nullptr, "GetAnalogStickManualCalibrationStage"},
117 {827, nullptr, "IsAnalogStickButtonPressed"},
118 {828, nullptr, "IsAnalogStickInReleasePosition"},
119 {829, nullptr, "IsAnalogStickInCircumference"},
120 {830, nullptr, "SetNotificationLedPattern"},
121 {831, nullptr, "SetNotificationLedPatternWithTimeout"},
122 {832, nullptr, "PrepareHidsForNotificationWake"},
123 {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"},
124 {851, nullptr, "EnableUsbFullKeyController"},
125 {852, nullptr, "IsUsbConnected"},
126 {870, &IHidSystemServer::IsHandheldButtonPressedOnConsoleMode, "IsHandheldButtonPressedOnConsoleMode"},
127 {900, nullptr, "ActivateInputDetector"},
128 {901, nullptr, "NotifyInputDetector"},
129 {1000, &IHidSystemServer::InitializeFirmwareUpdate, "InitializeFirmwareUpdate"},
130 {1001, nullptr, "GetFirmwareVersion"},
131 {1002, nullptr, "GetAvailableFirmwareVersion"},
132 {1003, nullptr, "IsFirmwareUpdateAvailable"},
133 {1004, nullptr, "CheckFirmwareUpdateRequired"},
134 {1005, nullptr, "StartFirmwareUpdate"},
135 {1006, nullptr, "AbortFirmwareUpdate"},
136 {1007, nullptr, "GetFirmwareUpdateState"},
137 {1008, nullptr, "ActivateAudioControl"},
138 {1009, nullptr, "AcquireAudioControlEventHandle"},
139 {1010, nullptr, "GetAudioControlStates"},
140 {1011, nullptr, "DeactivateAudioControl"},
141 {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"},
142 {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"},
143 {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"},
144 {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"},
145 {1100, nullptr, "GetHidbusSystemServiceObject"},
146 {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"},
147 {1130, nullptr, "InitializeUsbFirmwareUpdate"},
148 {1131, nullptr, "FinalizeUsbFirmwareUpdate"},
149 {1132, nullptr, "CheckUsbFirmwareUpdateRequired"},
150 {1133, nullptr, "StartUsbFirmwareUpdate"},
151 {1134, nullptr, "GetUsbFirmwareUpdateState"},
152 {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"},
153 {1150, nullptr, "SetTouchScreenMagnification"},
154 {1151, nullptr, "GetTouchScreenFirmwareVersion"},
155 {1152, nullptr, "SetTouchScreenDefaultConfiguration"},
156 {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"},
157 {1154, nullptr, "IsFirmwareAvailableForNotification"},
158 {1155, nullptr, "SetForceHandheldStyleVibration"},
159 {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"},
160 {1157, nullptr, "CancelConnectionTrigger"},
161 {1200, nullptr, "IsButtonConfigSupported"},
162 {1201, nullptr, "IsButtonConfigEmbeddedSupported"},
163 {1202, nullptr, "DeleteButtonConfig"},
164 {1203, nullptr, "DeleteButtonConfigEmbedded"},
165 {1204, nullptr, "SetButtonConfigEnabled"},
166 {1205, nullptr, "SetButtonConfigEmbeddedEnabled"},
167 {1206, nullptr, "IsButtonConfigEnabled"},
168 {1207, nullptr, "IsButtonConfigEmbeddedEnabled"},
169 {1208, nullptr, "SetButtonConfigEmbedded"},
170 {1209, nullptr, "SetButtonConfigFull"},
171 {1210, nullptr, "SetButtonConfigLeft"},
172 {1211, nullptr, "SetButtonConfigRight"},
173 {1212, nullptr, "GetButtonConfigEmbedded"},
174 {1213, nullptr, "GetButtonConfigFull"},
175 {1214, nullptr, "GetButtonConfigLeft"},
176 {1215, nullptr, "GetButtonConfigRight"},
177 {1250, nullptr, "IsCustomButtonConfigSupported"},
178 {1251, nullptr, "IsDefaultButtonConfigEmbedded"},
179 {1252, nullptr, "IsDefaultButtonConfigFull"},
180 {1253, nullptr, "IsDefaultButtonConfigLeft"},
181 {1254, nullptr, "IsDefaultButtonConfigRight"},
182 {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"},
183 {1256, nullptr, "IsButtonConfigStorageFullEmpty"},
184 {1257, nullptr, "IsButtonConfigStorageLeftEmpty"},
185 {1258, nullptr, "IsButtonConfigStorageRightEmpty"},
186 {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"},
187 {1260, nullptr, "GetButtonConfigStorageFullDeprecated"},
188 {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"},
189 {1262, nullptr, "GetButtonConfigStorageRightDeprecated"},
190 {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"},
191 {1264, nullptr, "SetButtonConfigStorageFullDeprecated"},
192 {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"},
193 {1266, nullptr, "SetButtonConfigStorageRightDeprecated"},
194 {1267, nullptr, "DeleteButtonConfigStorageEmbedded"},
195 {1268, nullptr, "DeleteButtonConfigStorageFull"},
196 {1269, nullptr, "DeleteButtonConfigStorageLeft"},
197 {1270, nullptr, "DeleteButtonConfigStorageRight"},
198 {1271, nullptr, "IsUsingCustomButtonConfig"},
199 {1272, nullptr, "IsAnyCustomButtonConfigEnabled"},
200 {1273, nullptr, "SetAllCustomButtonConfigEnabled"},
201 {1274, nullptr, "SetDefaultButtonConfig"},
202 {1275, nullptr, "SetAllDefaultButtonConfig"},
203 {1276, nullptr, "SetHidButtonConfigEmbedded"},
204 {1277, nullptr, "SetHidButtonConfigFull"},
205 {1278, nullptr, "SetHidButtonConfigLeft"},
206 {1279, nullptr, "SetHidButtonConfigRight"},
207 {1280, nullptr, "GetHidButtonConfigEmbedded"},
208 {1281, nullptr, "GetHidButtonConfigFull"},
209 {1282, nullptr, "GetHidButtonConfigLeft"},
210 {1283, nullptr, "GetHidButtonConfigRight"},
211 {1284, nullptr, "GetButtonConfigStorageEmbedded"},
212 {1285, nullptr, "GetButtonConfigStorageFull"},
213 {1286, nullptr, "GetButtonConfigStorageLeft"},
214 {1287, nullptr, "GetButtonConfigStorageRight"},
215 {1288, nullptr, "SetButtonConfigStorageEmbedded"},
216 {1289, nullptr, "SetButtonConfigStorageFull"},
217 {1290, nullptr, "DeleteButtonConfigStorageRight"},
218 {1291, nullptr, "DeleteButtonConfigStorageRight"},
219 };
220 // clang-format on
221
222 RegisterHandlers(functions);
223
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");
231}
232
233IHidSystemServer::~IHidSystemServer() {
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);
238};
239
240void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
241 LOG_WARNING(Service_HID, "called");
242
243 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy();
244
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");
258
259 IPC::ResponseBuilder rb{ctx, 2};
260 rb.Push(ResultSuccess);
261}
262
263void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
264 LOG_DEBUG(Service_HID, "(STUBBED) called"); // Spams a lot when controller applet is running
265
266 IPC::ResponseBuilder rb{ctx, 3};
267 rb.Push(ResultSuccess);
268 rb.PushEnum(system.HIDCore().GetLastActiveController());
269}
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
397void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) {
398 IPC::RequestParser rp{ctx};
399 const auto npad_id_type{rp.PopEnum<Core::HID::NpadIdType>()};
400
401 LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}",
402 npad_id_type); // Spams a lot when controller applet is running
403
404 const std::vector<Core::HID::UniquePadId> unique_pads{};
405
406 if (!unique_pads.empty()) {
407 ctx.WriteBuffer(unique_pads);
408 }
409
410 IPC::ResponseBuilder rb{ctx, 3};
411 rb.Push(ResultSuccess);
412 rb.Push(static_cast<u32>(unique_pads.size()));
413}
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
474void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) {
475 LOG_INFO(Service_AM, "called");
476
477 IPC::ResponseBuilder rb{ctx, 2, 1};
478 rb.Push(ResultSuccess);
479 rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
480}
481
482void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
483 const bool is_enabled = false;
484
485 LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled);
486
487 IPC::ResponseBuilder rb{ctx, 3};
488 rb.Push(ResultSuccess);
489 rb.Push(is_enabled);
490}
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
517void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) {
518 LOG_WARNING(Service_HID, "(STUBBED) called");
519
520 Core::HID::TouchScreenConfigurationForNx touchscreen_config{
521 .mode = Core::HID::TouchScreenModeForNx::Finger,
522 };
523
524 if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 &&
525 touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) {
526 touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting;
527 }
528
529 IPC::ResponseBuilder rb{ctx, 6};
530 rb.Push(ResultSuccess);
531 rb.PushRaw(touchscreen_config);
532}
533
534std::shared_ptr<ResourceManager> IHidSystemServer::GetResourceManager() {
535 resource_manager->Initialize();
536 return resource_manager;
537}
538
539} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h
new file mode 100644
index 000000000..822d5e5b9
--- /dev/null
+++ b/src/core/hle/service/hid/hid_system_server.h
@@ -0,0 +1,63 @@
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/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core {
10class System;
11}
12
13namespace Kernel {
14class KEvent;
15}
16
17namespace Service::HID {
18class ResourceManager;
19
20class IHidSystemServer final : public ServiceFramework<IHidSystemServer> {
21public:
22 explicit IHidSystemServer(Core::System& system_, std::shared_ptr<ResourceManager> resource);
23 ~IHidSystemServer() override;
24
25private:
26 void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx);
27 void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx);
28 void DisableAssigningSingleOnSlSrPress(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);
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);
46 void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx);
47 void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx);
48 void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx);
49 void InitializeFirmwareUpdate(HLERequestContext& ctx);
50 void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx);
51 void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx);
52
53 std::shared_ptr<ResourceManager> GetResourceManager();
54
55 Kernel::KEvent* acquire_connection_trigger_timeout_event;
56 Kernel::KEvent* acquire_device_registered_event;
57 Kernel::KEvent* joy_detach_event;
58 Kernel::KEvent* unique_pad_connection_event;
59 KernelHelpers::ServiceContext service_context;
60 std::shared_ptr<ResourceManager> resource_manager;
61};
62
63} // namespace Service::HID
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/irs.h b/src/core/hle/service/hid/irs.h
index a8fa19025..c8e6dab17 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -3,15 +3,12 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/core.h"
6#include "core/hid/hid_types.h" 7#include "core/hid/hid_types.h"
7#include "core/hid/irs_types.h" 8#include "core/hid/irs_types.h"
8#include "core/hle/service/hid/irsensor/processor_base.h" 9#include "core/hle/service/hid/irsensor/processor_base.h"
9#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
10 11
11namespace Core {
12class System;
13}
14
15namespace Core::HID { 12namespace Core::HID {
16class EmulatedController; 13class EmulatedController;
17} // namespace Core::HID 14} // namespace Core::HID
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
new file mode 100644
index 000000000..e76d4eea9
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -0,0 +1,241 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/kernel/k_shared_memory.h"
9#include "core/hle/service/hid/resource_manager.h"
10#include "core/hle/service/ipc_helpers.h"
11
12#include "core/hle/service/hid/controllers/console_six_axis.h"
13#include "core/hle/service/hid/controllers/debug_pad.h"
14#include "core/hle/service/hid/controllers/gesture.h"
15#include "core/hle/service/hid/controllers/keyboard.h"
16#include "core/hle/service/hid/controllers/mouse.h"
17#include "core/hle/service/hid/controllers/npad.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"
21#include "core/hle/service/hid/controllers/stubbed.h"
22#include "core/hle/service/hid/controllers/touchscreen.h"
23#include "core/hle/service/hid/controllers/xpad.h"
24
25namespace Service::HID {
26
27// Updating period for each HID device.
28// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
29// Correct npad_update_ns is 4ms this is overclocked to lower input lag
30constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
31constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
32constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
33constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
34
35ResourceManager::ResourceManager(Core::System& system_)
36 : system{system_}, service_context{system_, "hid"} {}
37
38ResourceManager::~ResourceManager() = default;
39
40void ResourceManager::Initialize() {
41 if (is_initialized) {
42 return;
43 }
44
45 u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
46 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory);
47 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory);
48 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory);
49 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory);
50 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory);
51 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory, service_context);
52 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory);
53 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory);
54 xpad = std::make_shared<XPad>(system.HIDCore(), shared_memory);
55
56 palma = std::make_shared<Palma>(system.HIDCore(), shared_memory, service_context);
57
58 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory);
59 sleep_button = std::make_shared<SleepButton>(system.HIDCore(), 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);
71
72 // Homebrew doesn't try to activate some controllers, so we activate them by default
73 npad->Activate();
74 six_axis->Activate();
75 touch_screen->Activate();
76
77 system.HIDCore().ReloadInputDevices();
78 is_initialized = true;
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}
139
140void ResourceManager::UpdateControllers(std::uintptr_t user_data,
141 std::chrono::nanoseconds ns_late) {
142 auto& core_timing = system.CoreTiming();
143 debug_pad->OnUpdate(core_timing);
144 unique_pad->OnUpdate(core_timing);
145 gesture->OnUpdate(core_timing);
146 touch_screen->OnUpdate(core_timing);
147 palma->OnUpdate(core_timing);
148 home_button->OnUpdate(core_timing);
149 sleep_button->OnUpdate(core_timing);
150 capture_button->OnUpdate(core_timing);
151 xpad->OnUpdate(core_timing);
152}
153
154void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
155 auto& core_timing = system.CoreTiming();
156 npad->OnUpdate(core_timing);
157}
158
159void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data,
160 std::chrono::nanoseconds ns_late) {
161 auto& core_timing = system.CoreTiming();
162 mouse->OnUpdate(core_timing);
163 debug_mouse->OnUpdate(core_timing);
164 keyboard->OnUpdate(core_timing);
165}
166
167void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
168 auto& core_timing = system.CoreTiming();
169 six_axis->OnUpdate(core_timing);
170 seven_six_axis->OnUpdate(core_timing);
171 console_six_axis->OnUpdate(core_timing);
172}
173
174IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
175 : ServiceFramework{system_, "IAppletResource"} {
176 static const FunctionInfo functions[] = {
177 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
178 };
179 RegisterHandlers(functions);
180
181 resource->Initialize();
182
183 // Register update callbacks
184 npad_update_event = Core::Timing::CreateEvent(
185 "HID::UpdatePadCallback",
186 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
187 -> std::optional<std::chrono::nanoseconds> {
188 const auto guard = LockService();
189 resource->UpdateNpad(user_data, ns_late);
190 return std::nullopt;
191 });
192 default_update_event = Core::Timing::CreateEvent(
193 "HID::UpdateDefaultCallback",
194 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
195 -> std::optional<std::chrono::nanoseconds> {
196 const auto guard = LockService();
197 resource->UpdateControllers(user_data, ns_late);
198 return std::nullopt;
199 });
200 mouse_keyboard_update_event = Core::Timing::CreateEvent(
201 "HID::UpdateMouseKeyboardCallback",
202 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
203 -> std::optional<std::chrono::nanoseconds> {
204 const auto guard = LockService();
205 resource->UpdateMouseKeyboard(user_data, ns_late);
206 return std::nullopt;
207 });
208 motion_update_event = Core::Timing::CreateEvent(
209 "HID::UpdateMotionCallback",
210 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)
211 -> std::optional<std::chrono::nanoseconds> {
212 const auto guard = LockService();
213 resource->UpdateMotion(user_data, ns_late);
214 return std::nullopt;
215 });
216
217 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
218 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
219 default_update_event);
220 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
221 mouse_keyboard_update_event);
222 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
223 motion_update_event);
224}
225
226IAppletResource::~IAppletResource() {
227 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
228 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
229 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
230 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
231}
232
233void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
234 LOG_DEBUG(Service_HID, "called");
235
236 IPC::ResponseBuilder rb{ctx, 2, 1};
237 rb.Push(ResultSuccess);
238 rb.PushCopyObjects(&system.Kernel().GetHidSharedMem());
239}
240
241} // namespace Service::HID
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
new file mode 100644
index 000000000..2b6a9b5e6
--- /dev/null
+++ b/src/core/hle/service/hid/resource_manager.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 "core/hle/service/kernel_helpers.h"
7#include "core/hle/service/service.h"
8
9namespace Core::Timing {
10struct EventType;
11}
12
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;
32
33class ResourceManager {
34
35public:
36 explicit ResourceManager(Core::System& system_);
37 ~ResourceManager();
38
39 void Initialize();
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
57 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
58 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
59 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
60 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
61
62private:
63 bool is_initialized{false};
64
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;
92
93 Core::System& system;
94 KernelHelpers::ServiceContext service_context;
95};
96
97class IAppletResource final : public ServiceFramework<IAppletResource> {
98public:
99 explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
100 ~IAppletResource() override;
101
102private:
103 void GetSharedMemoryHandle(HLERequestContext& ctx);
104
105 std::shared_ptr<Core::Timing::EventType> npad_update_event;
106 std::shared_ptr<Core::Timing::EventType> default_update_event;
107 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
108 std::shared_ptr<Core::Timing::EventType> motion_update_event;
109};
110
111} // namespace Service::HID
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/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/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 3da8cc3aa..7fd808f54 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -15,7 +15,7 @@
15 15
16namespace Service::android { 16namespace Service::android {
17 17
18struct GraphicBuffer; 18class GraphicBuffer;
19 19
20class BufferItem final { 20class BufferItem final {
21public: 21public:
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index 51291539d..d91886bed 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -5,7 +5,6 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvnflinger/buffer_item.h" 8#include "core/hle/service/nvnflinger/buffer_item.h"
10#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 9#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvnflinger/buffer_queue_core.h" 10#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -14,9 +13,8 @@
14 13
15namespace Service::android { 14namespace Service::android {
16 15
17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 16BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
18 Service::Nvidia::NvCore::NvMap& nvmap_) 17 : core{std::move(core_)}, slots{core->slots} {}
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
20 18
21BufferQueueConsumer::~BufferQueueConsumer() = default; 19BufferQueueConsumer::~BufferQueueConsumer() = default;
22 20
@@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
136 134
137 slots[slot].buffer_state = BufferState::Free; 135 slots[slot].buffer_state = BufferState::Free;
138 136
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
141 listener = core->connected_producer_listener; 137 listener = core->connected_producer_listener;
142 138
143 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); 139 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
175 return Status::NoError; 171 return Status::NoError;
176} 172}
177 173
174Status BufferQueueConsumer::Disconnect() {
175 LOG_DEBUG(Service_Nvnflinger, "called");
176
177 std::scoped_lock lock{core->mutex};
178
179 if (core->consumer_listener == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
181 return Status::BadValue;
182 }
183
184 core->is_abandoned = true;
185 core->consumer_listener = nullptr;
186 core->queue.clear();
187 core->FreeAllBuffersLocked();
188 core->SignalDequeueCondition();
189
190 return Status::NoError;
191}
192
178Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { 193Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
179 if (out_slot_mask == nullptr) { 194 if (out_slot_mask == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); 195 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 50ed0bb5f..0a61e8dbd 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -13,10 +13,6 @@
13#include "core/hle/service/nvnflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvnflinger/status.h" 14#include "core/hle/service/nvnflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
20namespace Service::android { 16namespace Service::android {
21 17
22class BufferItem; 18class BufferItem;
@@ -25,19 +21,18 @@ class IConsumerListener;
25 21
26class BufferQueueConsumer final { 22class BufferQueueConsumer final {
27public: 23public:
28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
29 Service::Nvidia::NvCore::NvMap& nvmap_);
30 ~BufferQueueConsumer(); 25 ~BufferQueueConsumer();
31 26
32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
33 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
34 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); 29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status Disconnect();
35 Status GetReleasedBuffers(u64* out_slot_mask); 31 Status GetReleasedBuffers(u64* out_slot_mask);
36 32
37private: 33private:
38 std::shared_ptr<BufferQueueCore> core; 34 std::shared_ptr<BufferQueueCore> core;
39 BufferQueueDefs::SlotsType& slots; 35 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
41}; 36};
42 37
43} // namespace Service::android 38} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index ed66f6f5b..4ed5e5978 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
14 14
15BufferQueueCore::~BufferQueueCore() = default; 15BufferQueueCore::~BufferQueueCore() = default;
16 16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() { 17void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true); 18 dequeue_possible.store(true);
27 dequeue_condition.notify_all(); 19 dequeue_condition.notify_all();
28} 20}
29 21
30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { 22bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
31 if (is_shutting_down) {
32 return false;
33 }
34
35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); 23 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false); 24 dequeue_possible.store(false);
37 25
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h
index 9164f08a0..e513d183b 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h
@@ -34,8 +34,6 @@ public:
34 BufferQueueCore(); 34 BufferQueueCore();
35 ~BufferQueueCore(); 35 ~BufferQueueCore();
36 36
37 void NotifyShutdown();
38
39private: 37private:
40 void SignalDequeueCondition(); 38 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); 39 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -74,7 +72,6 @@ private:
74 u32 transform_hint{}; 72 u32 transform_hint{};
75 bool is_allocating{}; 73 bool is_allocating{};
76 mutable std::condition_variable_any is_allocating_condition; 74 mutable std::condition_variable_any is_allocating_condition;
77 bool is_shutting_down{};
78}; 75};
79 76
80} // namespace Service::android 77} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index 6e7a49658..5d8762d25 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hle_ipc.h" 14#include "core/hle/service/hle_ipc.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/nvmap.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h" 16#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 17#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/consumer_listener.h" 18#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
533 item.is_droppable = core->dequeue_buffer_cannot_block || async; 532 item.is_droppable = core->dequeue_buffer_cannot_block || async;
534 item.swap_interval = swap_interval; 533 item.swap_interval = swap_interval;
535 534
536 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
537
538 sticky_transform = sticky_transform_; 535 sticky_transform = sticky_transform_;
539 536
540 if (core->queue.empty()) { 537 if (core->queue.empty()) {
@@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
744 return Status::NoError; 741 return Status::NoError;
745 } 742 }
746 743
747 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
748 // Allows synchronous destruction of nvmap handles.
749 for (auto& item : core->queue) {
750 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
751 }
752 core->queue.clear();
753
754 switch (api) { 744 switch (api) {
755 case NativeWindowApi::Egl: 745 case NativeWindowApi::Egl:
756 case NativeWindowApi::Cpu: 746 case NativeWindowApi::Cpu:
757 case NativeWindowApi::Media: 747 case NativeWindowApi::Media:
758 case NativeWindowApi::Camera: 748 case NativeWindowApi::Camera:
759 if (core->connected_api == api) { 749 if (core->connected_api == api) {
750 core->queue.clear();
760 core->FreeAllBuffersLocked(); 751 core->FreeAllBuffersLocked();
761 core->connected_producer_listener = nullptr; 752 core->connected_producer_listener = nullptr;
762 core->connected_api = NativeWindowApi::NoConnectedApi; 753 core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
785} 776}
786 777
787Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, 778Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
788 const std::shared_ptr<GraphicBuffer>& buffer) { 779 const std::shared_ptr<NvGraphicBuffer>& buffer) {
789 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); 780 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
790 781
791 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 782 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
796 787
797 slots[slot] = {}; 788 slots[slot] = {};
798 slots[slot].fence = Fence::NoFence(); 789 slots[slot].fence = Fence::NoFence();
799 slots[slot].graphic_buffer = buffer; 790 slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
800 slots[slot].frame_number = 0; 791 slots[slot].frame_number = 0;
801 792
802 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for 793 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
839 } 830 }
840 case TransactionId::SetPreallocatedBuffer: { 831 case TransactionId::SetPreallocatedBuffer: {
841 const auto slot = parcel_in.Read<s32>(); 832 const auto slot = parcel_in.Read<s32>();
842 const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); 833 const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
843 834
844 status = SetPreallocatedBuffer(slot, buffer); 835 status = SetPreallocatedBuffer(slot, buffer);
845 break; 836 break;
@@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
867 858
868 status = RequestBuffer(slot, &buf); 859 status = RequestBuffer(slot, &buf);
869 860
870 parcel_out.WriteFlattenedObject(buf); 861 parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
871 break; 862 break;
872 } 863 }
873 case TransactionId::QueueBuffer: { 864 case TransactionId::QueueBuffer: {
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index d4201c104..64c17d56c 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -38,6 +38,7 @@ namespace Service::android {
38 38
39class BufferQueueCore; 39class BufferQueueCore;
40class IProducerListener; 40class IProducerListener;
41struct NvGraphicBuffer;
41 42
42class BufferQueueProducer final : public IBinder { 43class BufferQueueProducer final : public IBinder {
43public: 44public:
@@ -65,7 +66,7 @@ public:
65 bool producer_controlled_by_app, QueueBufferOutput* output); 66 bool producer_controlled_by_app, QueueBufferOutput* output);
66 67
67 Status Disconnect(NativeWindowApi api); 68 Status Disconnect(NativeWindowApi api);
68 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); 69 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
69 70
70private: 71private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 72 BufferQueueProducer(const BufferQueueProducer&) = delete;
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d8c9dec3b..d25bca049 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -13,7 +13,7 @@
13 13
14namespace Service::android { 14namespace Service::android {
15 15
16struct GraphicBuffer; 16class GraphicBuffer;
17 17
18enum class BufferState : u32 { 18enum class BufferState : u32 {
19 Free = 0, 19 Free = 0,
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 4dcda8dac..1059e72bf 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app); 27 consumer->Connect(shared_from_this(), controlled_by_app);
28} 28}
29 29
30void ConsumerBase::Abandon() {
31 LOG_DEBUG(Service_Nvnflinger, "called");
32
33 std::scoped_lock lock{mutex};
34
35 if (!is_abandoned) {
36 this->AbandonLocked();
37 is_abandoned = true;
38 }
39}
40
41void ConsumerBase::AbandonLocked() {
42 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
43 this->FreeBufferLocked(i);
44 }
45 // disconnect from the BufferQueue
46 consumer->Disconnect();
47 consumer = nullptr;
48}
49
30void ConsumerBase::FreeBufferLocked(s32 slot_index) { 50void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); 51 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);
32 52
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index 264829414..ea3e9e97a 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -24,6 +24,7 @@ class BufferQueueConsumer;
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { 24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public: 25public:
26 void Connect(bool controlled_by_app); 26 void Connect(bool controlled_by_app);
27 void Abandon();
27 28
28protected: 29protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 30 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -34,6 +35,7 @@ protected:
34 void OnBuffersReleased() override; 35 void OnBuffersReleased() override;
35 void OnSidebandStreamChanged() override; 36 void OnSidebandStreamChanged() override;
36 37
38 void AbandonLocked();
37 void FreeBufferLocked(s32 slot_index); 39 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); 40 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); 41 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 6dc327b8b..d7db24f42 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
166}(); 166}();
167 167
168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { 168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
169 auto buffer = std::make_shared<android::GraphicBuffer>(); 169 auto buffer = std::make_shared<android::NvGraphicBuffer>();
170 buffer->width = SharedBufferWidth; 170 buffer->width = SharedBufferWidth;
171 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
172 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index bebb45eae..0745434c5 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
47 vsync_signal.Wait(); 47 vsync_signal.Wait();
48 48
49 const auto lock_guard = Lock(); 49 const auto lock_guard = Lock();
50 Compose(); 50
51 if (!is_abandoned) {
52 Compose();
53 }
51 } 54 }
52} 55}
53 56
@@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
98 } 101 }
99 102
100 ShutdownLayers(); 103 ShutdownLayers();
101 vsync_thread = {};
102 104
103 if (nvdrv) { 105 if (nvdrv) {
104 nvdrv->Close(disp_fd); 106 nvdrv->Close(disp_fd);
@@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
106} 108}
107 109
108void Nvnflinger::ShutdownLayers() { 110void Nvnflinger::ShutdownLayers() {
109 const auto lock_guard = Lock(); 111 // Abandon consumers.
110 for (auto& display : displays) { 112 {
111 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 113 const auto lock_guard = Lock();
112 display.GetLayer(layer).Core().NotifyShutdown(); 114 for (auto& display : displays) {
115 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
116 display.GetLayer(layer).GetConsumer().Abandon();
117 }
113 } 118 }
119
120 is_abandoned = true;
114 } 121 }
122
123 // Join the vsync thread, if it exists.
124 vsync_thread = {};
115} 125}
116 126
117void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 127void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 959d8b46b..f5d73acdb 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -140,6 +140,8 @@ private:
140 140
141 s32 swap_interval = 1; 141 s32 swap_interval = 1;
142 142
143 bool is_abandoned = false;
144
143 /// Event that handles screen composition. 145 /// Event that handles screen composition.
144 std::shared_ptr<Core::Timing::EventType> multi_composition_event; 146 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
145 std::shared_ptr<Core::Timing::EventType> single_composition_event; 147 std::shared_ptr<Core::Timing::EventType> single_composition_event;
diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h
index 7af166c40..3fa0fe15b 100644
--- a/src/core/hle/service/nvnflinger/status.h
+++ b/src/core/hle/service/nvnflinger/status.h
@@ -19,7 +19,7 @@ enum class Status : s32 {
19 Busy = -16, 19 Busy = -16,
20 NoInit = -19, 20 NoInit = -19,
21 BadValue = -22, 21 BadValue = -22,
22 InvalidOperation = -37, 22 InvalidOperation = -38,
23 BufferNeedsReallocation = 1, 23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2, 24 ReleaseAllBuffers = 2,
25}; 25};
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
new file mode 100644
index 000000000..ce70946ec
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/nvdrv/core/nvmap.h"
5#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
6
7namespace Service::android {
8
9static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
10 if (buffer) {
11 return *buffer;
12 } else {
13 return {};
14 }
15}
16
17GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
18 : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
19
20GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
21 std::shared_ptr<NvGraphicBuffer> buffer)
22 : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
23 if (this->BufferId() > 0) {
24 m_nvmap->DuplicateHandle(this->BufferId(), true);
25 }
26}
27
28GraphicBuffer::~GraphicBuffer() {
29 if (m_nvmap != nullptr && this->BufferId() > 0) {
30 m_nvmap->FreeHandle(this->BufferId(), true);
31 }
32}
33
34} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
index 3eac5cedd..da430aa75 100644
--- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
@@ -6,16 +6,22 @@
6 6
7#pragma once 7#pragma once
8 8
9#include <memory>
10
9#include "common/common_funcs.h" 11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "core/hle/service/nvnflinger/pixel_format.h" 13#include "core/hle/service/nvnflinger/pixel_format.h"
12 14
15namespace Service::Nvidia::NvCore {
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
13namespace Service::android { 19namespace Service::android {
14 20
15struct GraphicBuffer final { 21struct NvGraphicBuffer {
16 constexpr GraphicBuffer() = default; 22 constexpr NvGraphicBuffer() = default;
17 23
18 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) 24 constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
19 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, 25 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
20 usage{static_cast<s32>(usage_)} {} 26 usage{static_cast<s32>(usage_)} {}
21 27
@@ -93,6 +99,17 @@ struct GraphicBuffer final {
93 u32 offset{}; 99 u32 offset{};
94 INSERT_PADDING_WORDS(60); 100 INSERT_PADDING_WORDS(60);
95}; 101};
96static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); 102static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
103
104class GraphicBuffer final : public NvGraphicBuffer {
105public:
106 explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
107 explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
108 std::shared_ptr<NvGraphicBuffer> buffer);
109 ~GraphicBuffer();
110
111private:
112 Service::Nvidia::NvCore::NvMap* m_nvmap{};
113};
97 114
98} // namespace Service::android 115} // namespace Service::android
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index ec3af80af..f5edfdc8b 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
@@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx,
52 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); 41 FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId));
53 } 42 }
54 43
55 const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { 44 const auto early_exit_failure = [](std::string_view desc, Result code) {
56 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", 45 LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
57 desc); 46 desc);
58 IPC::ResponseBuilder rb{ctx, 2}; 47 return code;
59 rb.Push(code);
60 }; 48 };
61 49
62 const auto ver_file = romfs->GetFile("file"); 50 const auto ver_file = romfs->GetFile("file");
63 if (ver_file == nullptr) { 51 if (ver_file == nullptr) {
64 early_exit_failure("The system version archive didn't contain the file 'file'.", 52 return early_exit_failure("The system version archive didn't contain the file 'file'.",
65 FileSys::ERROR_INVALID_ARGUMENT); 53 FileSys::ERROR_INVALID_ARGUMENT);
66 return;
67 } 54 }
68 55
69 auto data = ver_file->ReadAllBytes(); 56 auto data = ver_file->ReadAllBytes();
70 if (data.size() != 0x100) { 57 if (data.size() != sizeof(FirmwareVersionFormat)) {
71 early_exit_failure("The system version file 'file' was not the correct size.", 58 return early_exit_failure("The system version file 'file' was not the correct size.",
72 FileSys::ERROR_OUT_OF_BOUNDS); 59 FileSys::ERROR_OUT_OF_BOUNDS);
73 return;
74 } 60 }
75 61
62 std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat));
63
76 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will 64 // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will
77 // zero out the REVISION_MINOR field. 65 // zero out the REVISION_MINOR field.
78 if (type == GetFirmwareVersionType::Version1) { 66 if (type == GetFirmwareVersionType::Version1) {
79 data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; 67 out_firmware.revision_minor = 0;
80 } 68 }
81 69
82 ctx.WriteBuffer(data); 70 return ResultSuccess;
83
84 IPC::ResponseBuilder rb{ctx, 2};
85 rb.Push(ResultSuccess);
86} 71}
87} // Anonymous namespace
88 72
89void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { 73void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
90 IPC::RequestParser rp{ctx}; 74 IPC::RequestParser rp{ctx};
@@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) {
98 82
99void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { 83void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) {
100 LOG_DEBUG(Service_SET, "called"); 84 LOG_DEBUG(Service_SET, "called");
101 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); 85
86 FirmwareVersionFormat firmware_data{};
87 const auto result =
88 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1);
89
90 if (result.IsSuccess()) {
91 ctx.WriteBuffer(firmware_data);
92 }
93
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(result);
102} 96}
103 97
104void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { 98void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) {
105 LOG_DEBUG(Service_SET, "called"); 99 LOG_DEBUG(Service_SET, "called");
106 GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); 100
101 FirmwareVersionFormat firmware_data{};
102 const auto result =
103 GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2);
104
105 if (result.IsSuccess()) {
106 ctx.WriteBuffer(firmware_data);
107 }
108
109 IPC::ResponseBuilder rb{ctx, 2};
110 rb.Push(result);
107} 111}
108 112
109void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { 113void SET_SYS::GetAccountSettings(HLERequestContext& ctx) {
@@ -431,8 +435,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) {
431void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { 435void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) {
432 u8 battery_percentage_flag{1}; 436 u8 battery_percentage_flag{1};
433 437
434 LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", 438 LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag);
435 battery_percentage_flag);
436 439
437 IPC::ResponseBuilder rb{ctx, 3}; 440 IPC::ResponseBuilder rb{ctx, 3};
438 rb.Push(ResultSuccess); 441 rb.Push(ResultSuccess);
@@ -492,6 +495,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) {
492 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); 495 rb.PushEnum(ChineseTraditionalInputMethod::Unknown0);
493} 496}
494 497
498void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) {
499 LOG_DEBUG(Service_SET, "(STUBBED) called");
500
501 const HomeMenuScheme default_color = {
502 .main = 0xFF323232,
503 .back = 0xFF323232,
504 .sub = 0xFFFFFFFF,
505 .bezel = 0xFFFFFFFF,
506 .extra = 0xFF000000,
507 };
508
509 IPC::ResponseBuilder rb{ctx, 7};
510 rb.Push(ResultSuccess);
511 rb.PushRaw(default_color);
512}
513
514void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) {
515 LOG_WARNING(Service_SET, "(STUBBED) called");
516
517 IPC::ResponseBuilder rb{ctx, 3};
518 rb.Push(ResultSuccess);
519 rb.Push(0);
520}
495void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { 521void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) {
496 LOG_WARNING(Service_SET, "(STUBBED) called"); 522 LOG_WARNING(Service_SET, "(STUBBED) called");
497 523
@@ -674,7 +700,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
674 {171, nullptr, "SetChineseTraditionalInputMethod"}, 700 {171, nullptr, "SetChineseTraditionalInputMethod"},
675 {172, nullptr, "GetPtmCycleCountReliability"}, 701 {172, nullptr, "GetPtmCycleCountReliability"},
676 {173, nullptr, "SetPtmCycleCountReliability"}, 702 {173, nullptr, "SetPtmCycleCountReliability"},
677 {174, nullptr, "GetHomeMenuScheme"}, 703 {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"},
678 {175, nullptr, "GetThemeSettings"}, 704 {175, nullptr, "GetThemeSettings"},
679 {176, nullptr, "SetThemeSettings"}, 705 {176, nullptr, "SetThemeSettings"},
680 {177, nullptr, "GetThemeKey"}, 706 {177, nullptr, "GetThemeKey"},
@@ -685,7 +711,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} {
685 {182, nullptr, "SetT"}, 711 {182, nullptr, "SetT"},
686 {183, nullptr, "GetPlatformRegion"}, 712 {183, nullptr, "GetPlatformRegion"},
687 {184, nullptr, "SetPlatformRegion"}, 713 {184, nullptr, "SetPlatformRegion"},
688 {185, nullptr, "GetHomeMenuSchemeModel"}, 714 {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"},
689 {186, nullptr, "GetMemoryUsageRateFlag"}, 715 {186, nullptr, "GetMemoryUsageRateFlag"},
690 {187, nullptr, "GetTouchScreenMode"}, 716 {187, nullptr, "GetTouchScreenMode"},
691 {188, nullptr, "SetTouchScreenMode"}, 717 {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/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index f0b5eff8a..d30f49877 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
35 return { 35 return {
36 buffer_queue_core, 36 buffer_queue_core,
37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), 37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; 38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
39} 39}
40 40
41Display::Display(u64 id, std::string name_, 41Display::Display(u64 id, std::string name_,
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 84b60a928..a3431772a 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,8 +1,10 @@
1// SPDX-FileCopyrightText: 2015 Citra Emulator Project 1// SPDX-FileCopyrightText: 2015 Citra Emulator Project
2// SPDX-FileCopyrightText: 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 3// SPDX-License-Identifier: GPL-2.0-or-later
3 4
4#include <algorithm> 5#include <algorithm>
5#include <cstring> 6#include <cstring>
7#include <mutex>
6#include <span> 8#include <span>
7 9
8#include "common/assert.h" 10#include "common/assert.h"
@@ -10,6 +12,7 @@
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/logging/log.h" 13#include "common/logging/log.h"
12#include "common/page_table.h" 14#include "common/page_table.h"
15#include "common/scope_exit.h"
13#include "common/settings.h" 16#include "common/settings.h"
14#include "common/swap.h" 17#include "common/swap.h"
15#include "core/core.h" 18#include "core/core.h"
@@ -318,7 +321,7 @@ struct Memory::Impl {
318 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 321 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
319 u8* const host_ptr) { 322 u8* const host_ptr) {
320 if constexpr (!UNSAFE) { 323 if constexpr (!UNSAFE) {
321 system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); 324 HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
322 } 325 }
323 std::memcpy(host_ptr, src_buffer, copy_amount); 326 std::memcpy(host_ptr, src_buffer, copy_amount);
324 }, 327 },
@@ -351,7 +354,7 @@ struct Memory::Impl {
351 }, 354 },
352 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount, 355 [&](const Common::ProcessAddress current_vaddr, const std::size_t copy_amount,
353 u8* const host_ptr) { 356 u8* const host_ptr) {
354 system.GPU().InvalidateRegion(GetInteger(current_vaddr), copy_amount); 357 HandleRasterizerWrite(GetInteger(current_vaddr), copy_amount);
355 std::memset(host_ptr, 0, copy_amount); 358 std::memset(host_ptr, 0, copy_amount);
356 }, 359 },
357 [](const std::size_t copy_amount) {}); 360 [](const std::size_t copy_amount) {});
@@ -420,7 +423,7 @@ struct Memory::Impl {
420 const std::size_t block_size) { 423 const std::size_t block_size) {
421 // dc cvac: Store to point of coherency 424 // dc cvac: Store to point of coherency
422 // CPU flush -> GPU invalidate 425 // CPU flush -> GPU invalidate
423 system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); 426 HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
424 }; 427 };
425 return PerformCacheOperation(dest_addr, size, on_rasterizer); 428 return PerformCacheOperation(dest_addr, size, on_rasterizer);
426 } 429 }
@@ -430,7 +433,7 @@ struct Memory::Impl {
430 const std::size_t block_size) { 433 const std::size_t block_size) {
431 // dc civac: Store to point of coherency, and invalidate from cache 434 // dc civac: Store to point of coherency, and invalidate from cache
432 // CPU flush -> GPU invalidate 435 // CPU flush -> GPU invalidate
433 system.GPU().InvalidateRegion(GetInteger(current_vaddr), block_size); 436 HandleRasterizerWrite(GetInteger(current_vaddr), block_size);
434 }; 437 };
435 return PerformCacheOperation(dest_addr, size, on_rasterizer); 438 return PerformCacheOperation(dest_addr, size, on_rasterizer);
436 } 439 }
@@ -767,7 +770,18 @@ struct Memory::Impl {
767 } 770 }
768 771
769 void HandleRasterizerWrite(VAddr address, size_t size) { 772 void HandleRasterizerWrite(VAddr address, size_t size) {
770 const size_t core = system.GetCurrentHostThreadID(); 773 constexpr size_t sys_core = Core::Hardware::NUM_CPU_CORES - 1;
774 const size_t core = std::min(system.GetCurrentHostThreadID(),
775 sys_core); // any other calls threads go to syscore.
776 // Guard on sys_core;
777 if (core == sys_core) [[unlikely]] {
778 sys_core_guard.lock();
779 }
780 SCOPE_EXIT({
781 if (core == sys_core) [[unlikely]] {
782 sys_core_guard.unlock();
783 }
784 });
771 auto& current_area = rasterizer_write_areas[core]; 785 auto& current_area = rasterizer_write_areas[core];
772 VAddr subaddress = address >> YUZU_PAGEBITS; 786 VAddr subaddress = address >> YUZU_PAGEBITS;
773 bool do_collection = current_area.last_address == subaddress; 787 bool do_collection = current_area.last_address == subaddress;
@@ -799,6 +813,7 @@ struct Memory::Impl {
799 rasterizer_read_areas{}; 813 rasterizer_read_areas{};
800 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; 814 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
801 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; 815 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
816 std::mutex sys_core_guard;
802}; 817};
803 818
804Memory::Memory(Core::System& system_) : system{system_} { 819Memory::Memory(Core::System& system_) : system{system_} {
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 53a89cc8f..db30ba598 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -10,7 +10,8 @@
10#include "core/hle/kernel/k_page_table.h" 10#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h" 11#include "core/hle/kernel/k_process.h"
12#include "core/hle/service/hid/controllers/npad.h" 12#include "core/hle/service/hid/controllers/npad.h"
13#include "core/hle/service/hid/hid.h" 13#include "core/hle/service/hid/hid_server.h"
14#include "core/hle/service/hid/resource_manager.h"
14#include "core/hle/service/sm/sm.h" 15#include "core/hle/service/sm/sm.h"
15#include "core/memory.h" 16#include "core/memory.h"
16#include "core/memory/cheat_engine.h" 17#include "core/memory/cheat_engine.h"
@@ -54,23 +55,20 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size)
54} 55}
55 56
56u64 StandardVmCallbacks::HidKeysDown() { 57u64 StandardVmCallbacks::HidKeysDown() {
57 const auto hid = system.ServiceManager().GetService<Service::HID::Hid>("hid"); 58 const auto hid = system.ServiceManager().GetService<Service::HID::IHidServer>("hid");
58 if (hid == nullptr) { 59 if (hid == nullptr) {
59 LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); 60 LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!");
60 return 0; 61 return 0;
61 } 62 }
62 63
63 const auto applet_resource = hid->GetAppletResource(); 64 const auto applet_resource = hid->GetResourceManager();
64 if (applet_resource == nullptr) { 65 if (applet_resource == nullptr) {
65 LOG_WARNING(CheatEngine, 66 LOG_WARNING(CheatEngine,
66 "Attempted to read input state, but applet resource is not initialized!"); 67 "Attempted to read input state, but applet resource is not initialized!");
67 return 0; 68 return 0;
68 } 69 }
69 70
70 const auto press_state = 71 const auto press_state = applet_resource->GetNpad()->GetAndResetPressState();
71 applet_resource
72 ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
73 .GetAndResetPressState();
74 return static_cast<u64>(press_state & HID::NpadButton::All); 72 return static_cast<u64>(press_state & HID::NpadButton::All);
75} 73}
76 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..7474cb0f9
--- /dev/null
+++ b/src/frontend_common/config.cpp
@@ -0,0 +1,1008 @@
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 if (string.substr(0, 2) == "//") {
928 boost::replace_all(adjusted_string, "//", "/");
929 adjusted_string.insert(0, "/");
930 } else {
931 boost::replace_all(adjusted_string, "//", "/");
932 }
933
934 // Needed for backwards compatibility with QSettings deserialization
935 for (const auto& special_character : special_characters) {
936 if (adjusted_string.find(special_character) != std::string::npos) {
937 adjusted_string.insert(0, "\"");
938 adjusted_string.append("\"");
939 break;
940 }
941 }
942 return adjusted_string;
943}
944
945std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) {
946 if (array_stack.empty()) {
947 return std::string(GetGroup()).append(AdjustKey(key));
948 }
949
950 std::string array_key;
951 for (size_t i = 0; i < array_stack.size(); ++i) {
952 if (!array_stack[i].name.empty()) {
953 array_key.append(array_stack[i].name).append("\\");
954 }
955
956 if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) {
957 array_key.append(ToString(array_stack[i].index)).append("\\");
958 }
959 }
960 std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key));
961 return final_key;
962}
963
964int Config::BeginArray(const std::string& array) {
965 array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0});
966 const int size = config->GetLongValue(GetSection().c_str(),
967 GetFullKey(std::string("size"), true).c_str(), 0);
968 array_stack.back().size = size;
969 return size;
970}
971
972void Config::EndArray() {
973 // You can't end a config array before starting one
974 ASSERT(!array_stack.empty());
975
976 // Set the array size to 0 if the array is ended without changing the index
977 int size = 0;
978 if (array_stack.back().index != 0) {
979 size = array_stack.back().size;
980 }
981
982 // Write out the size to config
983 if (key_stack.size() == 1 && array_stack.back().name.empty()) {
984 // Edge-case where the first array created doesn't have a name
985 config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str());
986 } else {
987 const auto key = GetFullKey(std::string("size"), true);
988 config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str());
989 }
990
991 array_stack.pop_back();
992}
993
994void Config::SetArrayIndex(const int index) {
995 // You can't set the array index if you haven't started one yet
996 ASSERT(!array_stack.empty());
997
998 const int array_index = index + 1;
999
1000 // You can't exceed the known max size of the array by more than 1
1001 ASSERT(array_stack.front().size + 1 >= array_index);
1002
1003 // Change the config array size to the current index since you may want
1004 // to reduce the number of elements that you read back from the config
1005 // in the future.
1006 array_stack.back().size = array_index;
1007 array_stack.back().index = array_index;
1008}
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/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 3ad34884d..1ff296af5 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -415,7 +415,7 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
415 // This list is missing ZL/ZR since those are not considered buttons. 415 // This list is missing ZL/ZR since those are not considered buttons.
416 // We will add those afterwards 416 // We will add those afterwards
417 // This list also excludes any button that can't be really mapped 417 // This list also excludes any button that can't be really mapped
418 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12> 418 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 14>
419 switch_to_gcadapter_button = { 419 switch_to_gcadapter_button = {
420 std::pair{Settings::NativeButton::A, PadButton::ButtonA}, 420 std::pair{Settings::NativeButton::A, PadButton::ButtonA},
421 {Settings::NativeButton::B, PadButton::ButtonB}, 421 {Settings::NativeButton::B, PadButton::ButtonB},
@@ -426,8 +426,10 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p
426 {Settings::NativeButton::DUp, PadButton::ButtonUp}, 426 {Settings::NativeButton::DUp, PadButton::ButtonUp},
427 {Settings::NativeButton::DRight, PadButton::ButtonRight}, 427 {Settings::NativeButton::DRight, PadButton::ButtonRight},
428 {Settings::NativeButton::DDown, PadButton::ButtonDown}, 428 {Settings::NativeButton::DDown, PadButton::ButtonDown},
429 {Settings::NativeButton::SL, PadButton::TriggerL}, 429 {Settings::NativeButton::SLLeft, PadButton::TriggerL},
430 {Settings::NativeButton::SR, PadButton::TriggerR}, 430 {Settings::NativeButton::SRLeft, PadButton::TriggerR},
431 {Settings::NativeButton::SLRight, PadButton::TriggerL},
432 {Settings::NativeButton::SRRight, PadButton::TriggerR},
431 {Settings::NativeButton::R, PadButton::TriggerZ}, 433 {Settings::NativeButton::R, PadButton::TriggerZ},
432 }; 434 };
433 if (!params.Has("port")) { 435 if (!params.Has("port")) {
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 0aca5a3a3..72d2951f3 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -680,8 +680,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par
680 Common::ParamPackage sr_button_params = button_params; 680 Common::ParamPackage sr_button_params = button_params;
681 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL)); 681 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
682 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR)); 682 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
683 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); 683 mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params));
684 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); 684 mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params));
685 } 685 }
686 686
687 // Map SL and SR buttons for right joycons 687 // Map SL and SR buttons for right joycons
@@ -693,8 +693,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par
693 Common::ParamPackage sr_button_params = button_params; 693 Common::ParamPackage sr_button_params = button_params;
694 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL)); 694 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
695 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR)); 695 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
696 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); 696 mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params));
697 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); 697 mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params));
698 } 698 }
699 699
700 return mapping; 700 return mapping;
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 66e3ae9af..78f458afe 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -828,16 +828,18 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p
828ButtonBindings SDLDriver::GetDefaultButtonBinding( 828ButtonBindings SDLDriver::GetDefaultButtonBinding(
829 const std::shared_ptr<SDLJoystick>& joystick) const { 829 const std::shared_ptr<SDLJoystick>& joystick) const {
830 // Default SL/SR mapping for other controllers 830 // Default SL/SR mapping for other controllers
831 auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; 831 auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
832 auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; 832 auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
833 auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER;
834 auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER;
833 835
834 if (joystick->IsJoyconLeft()) { 836 if (joystick->IsJoyconLeft()) {
835 sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; 837 sll_button = SDL_CONTROLLER_BUTTON_PADDLE2;
836 sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; 838 srl_button = SDL_CONTROLLER_BUTTON_PADDLE4;
837 } 839 }
838 if (joystick->IsJoyconRight()) { 840 if (joystick->IsJoyconRight()) {
839 sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; 841 slr_button = SDL_CONTROLLER_BUTTON_PADDLE3;
840 sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; 842 srr_button = SDL_CONTROLLER_BUTTON_PADDLE1;
841 } 843 }
842 844
843 return { 845 return {
@@ -855,8 +857,10 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding(
855 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, 857 {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
856 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, 858 {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
857 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, 859 {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
858 {Settings::NativeButton::SL, sl_button}, 860 {Settings::NativeButton::SLLeft, sll_button},
859 {Settings::NativeButton::SR, sr_button}, 861 {Settings::NativeButton::SRLeft, srl_button},
862 {Settings::NativeButton::SLRight, slr_button},
863 {Settings::NativeButton::SRRight, srr_button},
860 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, 864 {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
861 {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, 865 {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1},
862 }; 866 };
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index fcba4e3c6..08e49a0da 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -24,7 +24,7 @@ namespace InputCommon {
24class SDLJoystick; 24class SDLJoystick;
25 25
26using ButtonBindings = 26using ButtonBindings =
27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 18>; 27 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 20>;
28using ZButtonBindings = 28using ZButtonBindings =
29 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>; 29 std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
30 30
diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp
index 77db60e92..60821b31a 100644
--- a/src/input_common/drivers/udp_client.cpp
+++ b/src/input_common/drivers/udp_client.cpp
@@ -396,7 +396,7 @@ std::vector<Common::ParamPackage> UDPClient::GetInputDevices() const {
396 396
397ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { 397ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) {
398 // This list excludes any button that can't be really mapped 398 // This list excludes any button that can't be really mapped
399 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20> 399 static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 22>
400 switch_to_dsu_button = { 400 switch_to_dsu_button = {
401 std::pair{Settings::NativeButton::A, PadButton::Circle}, 401 std::pair{Settings::NativeButton::A, PadButton::Circle},
402 {Settings::NativeButton::B, PadButton::Cross}, 402 {Settings::NativeButton::B, PadButton::Cross},
@@ -412,8 +412,10 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p
412 {Settings::NativeButton::R, PadButton::R1}, 412 {Settings::NativeButton::R, PadButton::R1},
413 {Settings::NativeButton::ZL, PadButton::L2}, 413 {Settings::NativeButton::ZL, PadButton::L2},
414 {Settings::NativeButton::ZR, PadButton::R2}, 414 {Settings::NativeButton::ZR, PadButton::R2},
415 {Settings::NativeButton::SL, PadButton::L2}, 415 {Settings::NativeButton::SLLeft, PadButton::L2},
416 {Settings::NativeButton::SR, PadButton::R2}, 416 {Settings::NativeButton::SRLeft, PadButton::R2},
417 {Settings::NativeButton::SLRight, PadButton::L2},
418 {Settings::NativeButton::SRRight, PadButton::R2},
417 {Settings::NativeButton::LStick, PadButton::L3}, 419 {Settings::NativeButton::LStick, PadButton::L3},
418 {Settings::NativeButton::RStick, PadButton::R3}, 420 {Settings::NativeButton::RStick, PadButton::R3},
419 {Settings::NativeButton::Home, PadButton::Home}, 421 {Settings::NativeButton::Home, PadButton::Home},
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 83b763447..19db17c6d 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -231,6 +231,7 @@ add_library(shader_recompiler STATIC
231 ir_opt/rescaling_pass.cpp 231 ir_opt/rescaling_pass.cpp
232 ir_opt/ssa_rewrite_pass.cpp 232 ir_opt/ssa_rewrite_pass.cpp
233 ir_opt/texture_pass.cpp 233 ir_opt/texture_pass.cpp
234 ir_opt/vendor_workaround_pass.cpp
234 ir_opt/verification_pass.cpp 235 ir_opt/verification_pass.cpp
235 object_pool.h 236 object_pool.h
236 precompiled_headers.h 237 precompiled_headers.h
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index d0e308124..64e7bad75 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -559,12 +559,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
559 const IR::Value& offset, const IR::Value& lod_clamp) { 559 const IR::Value& offset, const IR::Value& lod_clamp) {
560 const auto info{inst.Flags<IR::TextureInstInfo>()}; 560 const auto info{inst.Flags<IR::TextureInstInfo>()};
561 ScopedRegister dpdx, dpdy, coords; 561 ScopedRegister dpdx, dpdy, coords;
562 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 562 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
563 if (multi_component) { 563 if (multi_component) {
564 // Allocate this early to avoid aliasing other registers 564 // Allocate this early to avoid aliasing other registers
565 dpdx = ScopedRegister{ctx.reg_alloc}; 565 dpdx = ScopedRegister{ctx.reg_alloc};
566 dpdy = ScopedRegister{ctx.reg_alloc}; 566 dpdy = ScopedRegister{ctx.reg_alloc};
567 if (info.num_derivates >= 3) { 567 if (info.num_derivatives >= 3) {
568 coords = ScopedRegister{ctx.reg_alloc}; 568 coords = ScopedRegister{ctx.reg_alloc};
569 } 569 }
570 } 570 }
@@ -584,7 +584,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, 584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
585 dpdy.reg, derivatives_vec); 585 dpdy.reg, derivatives_vec);
586 Register final_coord; 586 Register final_coord;
587 if (info.num_derivates >= 3) { 587 if (info.num_derivatives >= 3) {
588 ctx.Add("MOV.F {}.z,{}.x;" 588 ctx.Add("MOV.F {}.z,{}.x;"
589 "MOV.F {}.z,{}.y;", 589 "MOV.F {}.z,{}.y;",
590 dpdx.reg, coord_vec, dpdy.reg, coord_vec); 590 dpdx.reg, coord_vec, dpdy.reg, coord_vec);
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index d9872ecc2..6e940bd5a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -548,15 +548,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
548 if (sparse_inst) { 548 if (sparse_inst) {
549 throw NotImplementedException("EmitImageGradient Sparse"); 549 throw NotImplementedException("EmitImageGradient Sparse");
550 } 550 }
551 if (!offset.IsEmpty() && info.num_derivates <= 2) { 551 if (!offset.IsEmpty() && info.num_derivatives <= 2) {
552 throw NotImplementedException("EmitImageGradient offset"); 552 throw NotImplementedException("EmitImageGradient offset");
553 } 553 }
554 const auto texture{Texture(ctx, info, index)}; 554 const auto texture{Texture(ctx, info, index)};
555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; 555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
556 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 556 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; 557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
558 if (multi_component) { 558 if (multi_component) {
559 if (info.num_derivates >= 3) { 559 if (info.num_derivatives >= 3) {
560 const auto offset_vec{ctx.var_alloc.Consume(offset)}; 560 const auto offset_vec{ctx.var_alloc.Consume(offset)};
561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, 561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture,
562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); 562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
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/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 8decdf399..22ceca19c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -67,22 +67,22 @@ public:
67 } 67 }
68 } 68 }
69 69
70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, 70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
71 Id offset, Id lod_clamp) { 71 u32 num_derivatives, Id offset, Id lod_clamp) {
72 if (!Sirit::ValidId(derivates)) { 72 if (!Sirit::ValidId(derivatives)) {
73 throw LogicError("Derivates must be present"); 73 throw LogicError("Derivatives must be present");
74 } 74 }
75 boost::container::static_vector<Id, 3> deriv_x_accum; 75 boost::container::static_vector<Id, 3> deriv_x_accum;
76 boost::container::static_vector<Id, 3> deriv_y_accum; 76 boost::container::static_vector<Id, 3> deriv_y_accum;
77 for (u32 i = 0; i < num_derivates; ++i) { 77 for (u32 i = 0; i < num_derivatives; ++i) {
78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); 78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2));
79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); 79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1));
80 } 80 }
81 const Id derivates_X{ctx.OpCompositeConstruct( 81 const Id derivatives_X{ctx.OpCompositeConstruct(
82 ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; 82 ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
83 const Id derivates_Y{ctx.OpCompositeConstruct( 83 const Id derivatives_Y{ctx.OpCompositeConstruct(
84 ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; 84 ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
85 Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); 85 Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
86 if (Sirit::ValidId(offset)) { 86 if (Sirit::ValidId(offset)) {
87 Add(spv::ImageOperandsMask::Offset, offset); 87 Add(spv::ImageOperandsMask::Offset, offset);
88 } 88 }
@@ -91,26 +91,26 @@ public:
91 } 91 }
92 } 92 }
93 93
94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, 94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
95 Id offset, Id lod_clamp) { 95 Id offset, Id lod_clamp) {
96 if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { 96 if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
97 throw LogicError("Derivates must be present"); 97 throw LogicError("Derivatives must be present");
98 } 98 }
99 boost::container::static_vector<Id, 3> deriv_1_accum{ 99 boost::container::static_vector<Id, 3> deriv_1_accum{
100 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), 100 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 0),
101 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), 101 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 2),
102 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), 102 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 0),
103 }; 103 };
104 boost::container::static_vector<Id, 3> deriv_2_accum{ 104 boost::container::static_vector<Id, 3> deriv_2_accum{
105 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), 105 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 1),
106 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), 106 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 3),
107 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), 107 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 1),
108 }; 108 };
109 const Id derivates_id1{ctx.OpCompositeConstruct( 109 const Id derivatives_id1{ctx.OpCompositeConstruct(
110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; 110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})};
111 const Id derivates_id2{ctx.OpCompositeConstruct( 111 const Id derivatives_id2{ctx.OpCompositeConstruct(
112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; 112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
113 Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); 113 Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
114 if (Sirit::ValidId(offset)) { 114 if (Sirit::ValidId(offset)) {
115 Add(spv::ImageOperandsMask::Offset, offset); 115 Add(spv::ImageOperandsMask::Offset, offset);
116 } 116 }
@@ -548,12 +548,12 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
548} 548}
549 549
550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
551 Id derivates, Id offset, Id lod_clamp) { 551 Id derivatives, Id offset, Id lod_clamp) {
552 const auto info{inst->Flags<IR::TextureInstInfo>()}; 552 const auto info{inst->Flags<IR::TextureInstInfo>()};
553 const auto operands = 553 const auto operands =
554 info.num_derivates == 3 554 info.num_derivatives == 3
555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) 555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp)
556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, 556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset,
557 lod_clamp); 557 lod_clamp);
558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index a440b557d..7d34575c8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
543 const IR::Value& skip_mips); 543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivatives, Id offset, Id lod_clamp);
547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); 548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); 549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index b7caa4246..49171c470 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1864,11 +1864,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture
1864 return Inst(op, Flags{info}, handle, coords); 1864 return Inst(op, Flags{info}, handle, coords);
1865} 1865}
1866 1866
1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, 1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives,
1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) { 1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) {
1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient 1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient
1870 : Opcode::BindlessImageGradient}; 1870 : Opcode::BindlessImageGradient};
1871 return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); 1871 return Inst(op, Flags{info}, handle, coords, derivatives, offset, lod_clamp);
1872} 1872}
1873 1873
1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { 1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index f3c81dbe1..6c30897f4 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -335,7 +335,7 @@ public:
335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, 335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
336 const U32& lod, const U32& multisampling, TextureInstInfo info); 336 const U32& lod, const U32& multisampling, TextureInstInfo info);
337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, 337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
338 const Value& derivates, const Value& offset, 338 const Value& derivatives, const Value& offset,
339 const F32& lod_clamp, TextureInstInfo info); 339 const F32& lod_clamp, TextureInstInfo info);
340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); 340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
341 void ImageWrite(const Value& handle, const Value& coords, const Value& color, 341 void ImageWrite(const Value& handle, const Value& coords, const Value& color,
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h
index 1e9e8c8f5..c20c2401f 100644
--- a/src/shader_recompiler/frontend/ir/modifiers.h
+++ b/src/shader_recompiler/frontend/ir/modifiers.h
@@ -40,7 +40,7 @@ union TextureInstInfo {
40 BitField<21, 1, u32> has_lod_clamp; 40 BitField<21, 1, u32> has_lod_clamp;
41 BitField<22, 1, u32> relaxed_precision; 41 BitField<22, 1, u32> relaxed_precision;
42 BitField<23, 2, u32> gather_component; 42 BitField<23, 2, u32> gather_component;
43 BitField<25, 2, u32> num_derivates; 43 BitField<25, 2, u32> num_derivatives;
44 BitField<27, 3, ImageFormat> image_format; 44 BitField<27, 3, ImageFormat> image_format;
45 BitField<30, 1, u32> ndv_is_active; 45 BitField<30, 1, u32> ndv_is_active;
46}; 46};
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
index dd34507bc..4ce3dd0cd 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
@@ -59,7 +59,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
59 BitField<51, 3, IR::Pred> sparse_pred; 59 BitField<51, 3, IR::Pred> sparse_pred;
60 BitField<0, 8, IR::Reg> dest_reg; 60 BitField<0, 8, IR::Reg> dest_reg;
61 BitField<8, 8, IR::Reg> coord_reg; 61 BitField<8, 8, IR::Reg> coord_reg;
62 BitField<20, 8, IR::Reg> derivate_reg; 62 BitField<20, 8, IR::Reg> derivative_reg;
63 BitField<28, 3, TextureType> type; 63 BitField<28, 3, TextureType> type;
64 BitField<31, 4, u64> mask; 64 BitField<31, 4, u64> mask;
65 BitField<36, 13, u64> cbuf_offset; 65 BitField<36, 13, u64> cbuf_offset;
@@ -71,7 +71,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
71 } 71 }
72 72
73 IR::Value coords; 73 IR::Value coords;
74 u32 num_derivates{}; 74 u32 num_derivatives{};
75 IR::Reg base_reg{txd.coord_reg}; 75 IR::Reg base_reg{txd.coord_reg};
76 IR::Reg last_reg; 76 IR::Reg last_reg;
77 IR::Value handle; 77 IR::Value handle;
@@ -90,42 +90,42 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
90 switch (txd.type) { 90 switch (txd.type) {
91 case TextureType::_1D: { 91 case TextureType::_1D: {
92 coords = v.F(base_reg); 92 coords = v.F(base_reg);
93 num_derivates = 1; 93 num_derivatives = 1;
94 last_reg = base_reg + 1; 94 last_reg = base_reg + 1;
95 break; 95 break;
96 } 96 }
97 case TextureType::ARRAY_1D: { 97 case TextureType::ARRAY_1D: {
98 last_reg = base_reg + 1; 98 last_reg = base_reg + 1;
99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); 99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array());
100 num_derivates = 1; 100 num_derivatives = 1;
101 break; 101 break;
102 } 102 }
103 case TextureType::_2D: { 103 case TextureType::_2D: {
104 last_reg = base_reg + 2; 104 last_reg = base_reg + 2;
105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); 105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1));
106 num_derivates = 2; 106 num_derivatives = 2;
107 break; 107 break;
108 } 108 }
109 case TextureType::ARRAY_2D: { 109 case TextureType::ARRAY_2D: {
110 last_reg = base_reg + 2; 110 last_reg = base_reg + 2;
111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); 111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array());
112 num_derivates = 2; 112 num_derivatives = 2;
113 break; 113 break;
114 } 114 }
115 default: 115 default:
116 throw NotImplementedException("Invalid texture type"); 116 throw NotImplementedException("Invalid texture type");
117 } 117 }
118 118
119 const IR::Reg derivate_reg{txd.derivate_reg}; 119 const IR::Reg derivative_reg{txd.derivative_reg};
120 IR::Value derivates; 120 IR::Value derivatives;
121 switch (num_derivates) { 121 switch (num_derivatives) {
122 case 1: { 122 case 1: {
123 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1)); 123 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1));
124 break; 124 break;
125 } 125 }
126 case 2: { 126 case 2: {
127 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1), 127 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1),
128 v.F(derivate_reg + 2), v.F(derivate_reg + 3)); 128 v.F(derivative_reg + 2), v.F(derivative_reg + 3));
129 break; 129 break;
130 } 130 }
131 default: 131 default:
@@ -150,9 +150,10 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
150 150
151 IR::TextureInstInfo info{}; 151 IR::TextureInstInfo info{};
152 info.type.Assign(GetType(txd.type)); 152 info.type.Assign(GetType(txd.type));
153 info.num_derivates.Assign(num_derivates); 153 info.num_derivatives.Assign(num_derivatives);
154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); 154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0);
155 const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; 155 const IR::Value sample{
156 v.ir.ImageGradient(handle, coords, derivatives, offset, lod_clamp, info)};
156 157
157 IR::Reg dest_reg{txd.dest_reg}; 158 IR::Reg dest_reg{txd.dest_reg};
158 for (size_t element = 0; element < 4; ++element) { 159 for (size_t element = 0; element < 4; ++element) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 928b35561..8fac6bad3 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -310,6 +310,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
310 } 310 }
311 Optimization::CollectShaderInfoPass(env, program); 311 Optimization::CollectShaderInfoPass(env, program);
312 Optimization::LayerPass(program, host_info); 312 Optimization::LayerPass(program, host_info);
313 Optimization::VendorWorkaroundPass(program);
313 314
314 CollectInterpolationInfo(env, program); 315 CollectInterpolationInfo(env, program);
315 AddNVNStorageBuffers(program); 316 AddNVNStorageBuffers(program);
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index f46e55122..ec12c843a 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -428,7 +428,7 @@ void FoldFPAdd32(IR::Inst& inst) {
428 } 428 }
429} 429}
430 430
431bool FoldDerivateYFromCorrection(IR::Inst& inst) { 431bool FoldDerivativeYFromCorrection(IR::Inst& inst) {
432 const IR::Value lhs_value{inst.Arg(0)}; 432 const IR::Value lhs_value{inst.Arg(0)};
433 const IR::Value rhs_value{inst.Arg(1)}; 433 const IR::Value rhs_value{inst.Arg(1)};
434 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 434 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -464,7 +464,7 @@ void FoldFPMul32(IR::Inst& inst) {
464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { 464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
465 return; 465 return;
466 } 466 }
467 if (FoldDerivateYFromCorrection(inst)) { 467 if (FoldDerivativeYFromCorrection(inst)) {
468 return; 468 return;
469 } 469 }
470 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 470 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -699,7 +699,7 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
699 } 699 }
700} 700}
701 701
702bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) { 702bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coord) {
703 if (coord.IsImmediate()) { 703 if (coord.IsImmediate()) {
704 return false; 704 return false;
705 } 705 }
@@ -834,7 +834,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
834 IR::Inst* const inst2 = coords.InstRecursive(); 834 IR::Inst* const inst2 = coords.InstRecursive();
835 std::array<std::array<IR::Value, 3>, 3> results_matrix; 835 std::array<std::array<IR::Value, 3>, 3> results_matrix;
836 for (size_t i = 0; i < 3; i++) { 836 for (size_t i = 0; i < 3; i++) {
837 if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) { 837 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
838 return; 838 return;
839 } 839 }
840 } 840 }
@@ -852,7 +852,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], 852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
853 results_matrix[1][1], results_matrix[1][2]); 853 results_matrix[1][1], results_matrix[1][2]);
854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); 854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]);
855 info.num_derivates.Assign(3); 855 info.num_derivatives.Assign(3);
856 IR::Value new_gradient_instruction = 856 IR::Value new_gradient_instruction =
857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); 857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info);
858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); 858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive();
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 629d18fa1..d4d5285e5 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -26,6 +26,7 @@ void SsaRewritePass(IR::Program& program);
26void PositionPass(Environment& env, IR::Program& program); 26void PositionPass(Environment& env, IR::Program& program);
27void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); 27void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
28void LayerPass(IR::Program& program, const HostTranslateInfo& host_info); 28void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
29void VendorWorkaroundPass(IR::Program& program);
29void VerificationPass(const IR::Program& program); 30void VerificationPass(const IR::Program& program);
30 31
31// Dual Vertex 32// Dual Vertex
diff --git a/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp
new file mode 100644
index 000000000..08c658cb8
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/vendor_workaround_pass.cpp
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "shader_recompiler/frontend/ir/basic_block.h"
5#include "shader_recompiler/frontend/ir/ir_emitter.h"
6#include "shader_recompiler/frontend/ir/value.h"
7#include "shader_recompiler/ir_opt/passes.h"
8
9namespace Shader::Optimization {
10
11namespace {
12void AddingByteSwapsWorkaround(IR::Block& block, IR::Inst& inst) {
13 /*
14 * Workaround for an NVIDIA bug seen in Super Mario RPG
15 *
16 * We are looking for this pattern:
17 * %lhs_bfe = BitFieldUExtract %factor_a, #0, #16
18 * %lhs_mul = IMul32 %lhs_bfe, %factor_b // potentially optional?
19 * %lhs_shl = ShiftLeftLogical32 %lhs_mul, #16
20 * %rhs_bfe = BitFieldUExtract %factor_a, #16, #16
21 * %result = IAdd32 %lhs_shl, %rhs_bfe
22 *
23 * And replacing the IAdd32 with a BitwiseOr32
24 * %result = BitwiseOr32 %lhs_shl, %rhs_bfe
25 *
26 */
27 IR::Inst* const lhs_shl{inst.Arg(0).TryInstRecursive()};
28 IR::Inst* const rhs_bfe{inst.Arg(1).TryInstRecursive()};
29 if (!lhs_shl || !rhs_bfe) {
30 return;
31 }
32 if (lhs_shl->GetOpcode() != IR::Opcode::ShiftLeftLogical32 ||
33 lhs_shl->Arg(1) != IR::Value{16U}) {
34 return;
35 }
36 if (rhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract || rhs_bfe->Arg(1) != IR::Value{16U} ||
37 rhs_bfe->Arg(2) != IR::Value{16U}) {
38 return;
39 }
40 IR::Inst* const lhs_mul{lhs_shl->Arg(0).TryInstRecursive()};
41 if (!lhs_mul) {
42 return;
43 }
44 const bool lhs_mul_optional{lhs_mul->GetOpcode() == IR::Opcode::BitFieldUExtract};
45 if (lhs_mul->GetOpcode() != IR::Opcode::IMul32 &&
46 lhs_mul->GetOpcode() != IR::Opcode::BitFieldUExtract) {
47 return;
48 }
49 IR::Inst* const lhs_bfe{lhs_mul_optional ? lhs_mul : lhs_mul->Arg(0).TryInstRecursive()};
50 if (!lhs_bfe) {
51 return;
52 }
53 if (lhs_bfe->GetOpcode() != IR::Opcode::BitFieldUExtract) {
54 return;
55 }
56 if (lhs_bfe->Arg(1) != IR::Value{0U} || lhs_bfe->Arg(2) != IR::Value{16U}) {
57 return;
58 }
59 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
60 inst.ReplaceUsesWith(ir.BitwiseOr(IR::U32{inst.Arg(0)}, IR::U32{inst.Arg(1)}));
61}
62
63} // Anonymous namespace
64
65void VendorWorkaroundPass(IR::Program& program) {
66 for (IR::Block* const block : program.post_order_blocks) {
67 for (IR::Inst& inst : block->Instructions()) {
68 switch (inst.GetOpcode()) {
69 case IR::Opcode::IAdd32:
70 AddingByteSwapsWorkaround(*block, inst);
71 break;
72 default:
73 break;
74 }
75 }
76 }
77}
78
79} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 38d820db2..a9de9f4a9 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
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 336532e0b..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()
@@ -67,6 +67,8 @@ add_library(video_core STATIC
67 host1x/codecs/vp9.cpp 67 host1x/codecs/vp9.cpp
68 host1x/codecs/vp9.h 68 host1x/codecs/vp9.h
69 host1x/codecs/vp9_types.h 69 host1x/codecs/vp9_types.h
70 host1x/ffmpeg/ffmpeg.cpp
71 host1x/ffmpeg/ffmpeg.h
70 host1x/control.cpp 72 host1x/control.cpp
71 host1x/control.h 73 host1x/control.h
72 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 813b68963..90dbd352f 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1209,11 +1209,6 @@ void BufferCache<P>::UpdateDrawIndirect() {
1209 .size = static_cast<u32>(size), 1209 .size = static_cast<u32>(size),
1210 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), 1210 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1211 }; 1211 };
1212 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
1213 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
1214 IntervalType interval{cpu_addr_start, cpu_addr_end};
1215 ClearDownload(interval);
1216 common_ranges.subtract(interval);
1217 }; 1212 };
1218 if (current_draw_indirect->include_count) { 1213 if (current_draw_indirect->include_count) {
1219 update(current_draw_indirect->count_start_address, sizeof(u32), 1214 update(current_draw_indirect->count_start_address, sizeof(u32),
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 02e161270..91f10aec2 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -72,7 +72,7 @@ void Fermi2D::Blit() {
72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); 72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
73 73
74 const auto& args = regs.pixels_from_memory; 74 const auto& args = regs.pixels_from_memory;
75 constexpr s64 null_derivate = 1ULL << 32; 75 constexpr s64 null_derivative = 1ULL << 32;
76 Surface src = regs.src; 76 Surface src = regs.src;
77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); 77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && 78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
@@ -89,7 +89,7 @@ void Fermi2D::Blit() {
89 .operation = regs.operation, 89 .operation = regs.operation,
90 .filter = args.sample_mode.filter, 90 .filter = args.sample_mode.filter,
91 .must_accelerate = 91 .must_accelerate =
92 args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, 92 args.du_dx != null_derivative || args.dv_dy != null_derivative || delegate_to_gpu,
93 .dst_x0 = args.dst_x0, 93 .dst_x0 = args.dst_x0,
94 .dst_y0 = args.dst_y0, 94 .dst_y0 = args.dst_y0,
95 .dst_x1 = args.dst_x0 + args.dst_width, 95 .dst_x1 = args.dst_x0 + args.dst_width,
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 32d767d85..592c28ba3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() {
268 std::numeric_limits<u32>::max()}; 268 std::numeric_limits<u32>::max()};
269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); 269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size); 270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
271 const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; 271 const size_t cap{GetMaxCurrentVertices() * 4 * byte_size};
272 const size_t lower_cap = 272 const size_t lower_cap =
273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); 273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap);
274 return std::min<size_t>( 274 return std::min<size_t>(
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index c0e6471fe..805a89900 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -86,10 +86,7 @@ public:
86 uncommitted_operations.emplace_back(std::move(func)); 86 uncommitted_operations.emplace_back(std::move(func));
87 } 87 }
88 pending_operations.emplace_back(std::move(uncommitted_operations)); 88 pending_operations.emplace_back(std::move(uncommitted_operations));
89 { 89 QueueFence(new_fence);
90 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
91 QueueFence(new_fence);
92 }
93 if (!delay_fence) { 90 if (!delay_fence) {
94 func(); 91 func();
95 } 92 }
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_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 44a771d65..af0a453ee 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -559,7 +559,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
559} 559}
560 560
561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { 561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
562 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); 562 const GLenum buffer_mode =
563 num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS;
564 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode);
563} 565}
564 566
565void GraphicsPipeline::GenerateTransformFeedbackState() { 567void GraphicsPipeline::GenerateTransformFeedbackState() {
@@ -567,12 +569,14 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
567 // when this is required. 569 // when this is required.
568 GLint* cursor{xfb_attribs.data()}; 570 GLint* cursor{xfb_attribs.data()};
569 571
572 num_xfb_buffers_active = 0;
570 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { 573 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
571 const auto& layout = key.xfb_state.layouts[feedback]; 574 const auto& layout = key.xfb_state.layouts[feedback];
572 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); 575 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
573 if (layout.varying_count == 0) { 576 if (layout.varying_count == 0) {
574 continue; 577 continue;
575 } 578 }
579 num_xfb_buffers_active++;
576 580
577 const auto& locations = key.xfb_state.varyings[feedback]; 581 const auto& locations = key.xfb_state.varyings[feedback];
578 std::optional<u32> current_index; 582 std::optional<u32> current_index;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 74fc9cc3d..2f70c1ae9 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -154,6 +154,7 @@ private:
154 154
155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
156 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
157 u32 num_xfb_buffers_active{};
157 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; 158 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
158 159
159 std::mutex built_mutex; 160 std::mutex built_mutex;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 27e2de1bf..9995b6dd4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -555,7 +555,7 @@ void RasterizerOpenGL::OnCacheInvalidation(VAddr addr, u64 size) {
555 } 555 }
556 { 556 {
557 std::scoped_lock lock{buffer_cache.mutex}; 557 std::scoped_lock lock{buffer_cache.mutex};
558 buffer_cache.CachedWriteMemory(addr, size); 558 buffer_cache.WriteMemory(addr, size);
559 } 559 }
560 shader_cache.InvalidateRegion(addr, size); 560 shader_cache.InvalidateRegion(addr, size);
561} 561}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7e7a80740..c4c30d807 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -132,16 +132,12 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
132 const bool use_accelerated = 132 const bool use_accelerated =
133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); 133 rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
134 const bool is_srgb = use_accelerated && screen_info.is_srgb; 134 const bool is_srgb = use_accelerated && screen_info.is_srgb;
135 RenderScreenshot(*framebuffer, use_accelerated);
135 136
136 { 137 Frame* frame = present_manager.GetRenderFrame();
137 std::scoped_lock lock{rasterizer.LockCaches()}; 138 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
138 RenderScreenshot(*framebuffer, use_accelerated); 139 scheduler.Flush(*frame->render_ready);
139 140 present_manager.Present(frame);
140 Frame* frame = present_manager.GetRenderFrame();
141 blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
142 scheduler.Flush(*frame->render_ready);
143 present_manager.Present(frame);
144 }
145 141
146 gpu.RendererFrameEndNotify(); 142 gpu.RendererFrameEndNotify();
147 rasterizer.TickFrame(); 143 rasterizer.TickFrame();
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 22bf8cc77..89b455bff 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
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 66c03bf17..078777cdd 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -211,6 +211,13 @@ public:
211 return; 211 return;
212 } 212 }
213 PauseCounter(); 213 PauseCounter();
214 const auto driver_id = device.GetDriverID();
215 if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
216 driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
217 pending_sync.clear();
218 sync_values_stash.clear();
219 return;
220 }
214 sync_values_stash.clear(); 221 sync_values_stash.clear();
215 sync_values_stash.emplace_back(); 222 sync_values_stash.emplace_back();
216 std::vector<HostSyncValues>* sync_values = &sync_values_stash.back(); 223 std::vector<HostSyncValues>* sync_values = &sync_values_stash.back();
@@ -1378,6 +1385,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku
1378 return true; 1385 return true;
1379 } 1386 }
1380 1387
1388 auto driver_id = impl->device.GetDriverID();
1389 if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
1390 driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) {
1391 return true;
1392 }
1393
1381 for (size_t i = 0; i < 2; i++) { 1394 for (size_t i = 0; i < 2; i++) {
1382 is_null[i] = !is_in_ac[i] && check_value(objects[i]->address); 1395 is_null[i] = !is_in_ac[i] && check_value(objects[i]->address);
1383 } 1396 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index c0e8431e4..e0ab1eaac 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -199,7 +199,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
199 if (!pipeline) { 199 if (!pipeline) {
200 return; 200 return;
201 } 201 }
202 std::scoped_lock lock{LockCaches()}; 202 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
203 // update engine as channel may be different. 203 // update engine as channel may be different.
204 pipeline->SetEngine(maxwell3d, gpu_memory); 204 pipeline->SetEngine(maxwell3d, gpu_memory);
205 pipeline->Configure(is_indexed); 205 pipeline->Configure(is_indexed);
@@ -621,7 +621,7 @@ void RasterizerVulkan::OnCacheInvalidation(VAddr addr, u64 size) {
621 } 621 }
622 { 622 {
623 std::scoped_lock lock{buffer_cache.mutex}; 623 std::scoped_lock lock{buffer_cache.mutex};
624 buffer_cache.CachedWriteMemory(addr, size); 624 buffer_cache.WriteMemory(addr, size);
625 } 625 }
626 pipeline_cache.InvalidateRegion(addr, size); 626 pipeline_cache.InvalidateRegion(addr, size);
627} 627}
@@ -710,7 +710,6 @@ void RasterizerVulkan::TiledCacheBarrier() {
710} 710}
711 711
712void RasterizerVulkan::FlushCommands() { 712void RasterizerVulkan::FlushCommands() {
713 std::scoped_lock lock{LockCaches()};
714 if (draw_counter == 0) { 713 if (draw_counter == 0) {
715 return; 714 return;
716 } 715 }
@@ -808,7 +807,6 @@ void RasterizerVulkan::FlushWork() {
808 if ((++draw_counter & 7) != 7) { 807 if ((++draw_counter & 7) != 7) {
809 return; 808 return;
810 } 809 }
811 std::scoped_lock lock{LockCaches()};
812 if (draw_counter < DRAWS_TO_DISPATCH) { 810 if (draw_counter < DRAWS_TO_DISPATCH) {
813 // Send recorded tasks to the worker thread 811 // Send recorded tasks to the worker thread
814 scheduler.DispatchWork(); 812 scheduler.DispatchWork();
@@ -1507,7 +1505,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
1507void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { 1505void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) {
1508 CreateChannel(channel); 1506 CreateChannel(channel);
1509 { 1507 {
1510 std::scoped_lock lock{LockCaches()}; 1508 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1511 texture_cache.CreateChannel(channel); 1509 texture_cache.CreateChannel(channel);
1512 buffer_cache.CreateChannel(channel); 1510 buffer_cache.CreateChannel(channel);
1513 } 1511 }
@@ -1520,7 +1518,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
1520 const s32 channel_id = channel.bind_id; 1518 const s32 channel_id = channel.bind_id;
1521 BindToChannel(channel_id); 1519 BindToChannel(channel_id);
1522 { 1520 {
1523 std::scoped_lock lock{LockCaches()}; 1521 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1524 texture_cache.BindToChannel(channel_id); 1522 texture_cache.BindToChannel(channel_id);
1525 buffer_cache.BindToChannel(channel_id); 1523 buffer_cache.BindToChannel(channel_id);
1526 } 1524 }
@@ -1533,7 +1531,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
1533void RasterizerVulkan::ReleaseChannel(s32 channel_id) { 1531void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
1534 EraseChannel(channel_id); 1532 EraseChannel(channel_id);
1535 { 1533 {
1536 std::scoped_lock lock{LockCaches()}; 1534 std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
1537 texture_cache.EraseChannel(channel_id); 1535 texture_cache.EraseChannel(channel_id);
1538 buffer_cache.EraseChannel(channel_id); 1536 buffer_cache.EraseChannel(channel_id);
1539 } 1537 }
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index ce3dfbaab..ad069556c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -133,10 +133,6 @@ public:
133 133
134 void ReleaseChannel(s32 channel_id) override; 134 void ReleaseChannel(s32 channel_id) override;
135 135
136 std::scoped_lock<std::recursive_mutex, std::recursive_mutex> LockCaches() {
137 return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex};
138 }
139
140private: 136private:
141 static constexpr size_t MAX_TEXTURES = 192; 137 static constexpr size_t MAX_TEXTURES = 192;
142 static constexpr size_t MAX_IMAGES = 48; 138 static constexpr size_t MAX_IMAGES = 48;
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index de34f6d49..5dbec2e62 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -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..edf5d7635 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -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/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index e518756d2..fde36a49c 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -635,6 +635,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
635 has_broken_cube_compatibility = true; 635 has_broken_cube_compatibility = true;
636 } 636 }
637 } 637 }
638 if (is_qualcomm) {
639 const u32 version = (properties.properties.driverVersion << 3) >> 3;
640 if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
641 has_broken_parallel_compiling = true;
642 }
643 }
638 if (extensions.sampler_filter_minmax && is_amd) { 644 if (extensions.sampler_filter_minmax && is_amd) {
639 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. 645 // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
640 if (!features.shader_float16_int8.shaderFloat16) { 646 if (!features.shader_float16_int8.shaderFloat16) {
@@ -863,7 +869,8 @@ bool Device::ShouldBoostClocks() const {
863 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA || 869 driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA ||
864 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP; 870 driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP;
865 871
866 const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F; 872 const bool is_steam_deck = (vendor_id == 0x1002 && device_id == 0x163F) ||
873 (vendor_id == 0x1002 && device_id == 0x1435);
867 874
868 const bool is_debugging = this->HasDebuggingToolAttached(); 875 const bool is_debugging = this->HasDebuggingToolAttached();
869 876
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/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/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index 515cb7ce6..9e5319716 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -13,7 +13,6 @@
13#include "core/hid/hid_core.h" 13#include "core/hid/hid_core.h"
14#include "core/hid/hid_types.h" 14#include "core/hid/hid_types.h"
15#include "core/hle/service/hid/controllers/npad.h" 15#include "core/hle/service/hid/controllers/npad.h"
16#include "core/hle/service/hid/hid.h"
17#include "core/hle/service/sm/sm.h" 16#include "core/hle/service/sm/sm.h"
18#include "ui_qt_controller.h" 17#include "ui_qt_controller.h"
19#include "yuzu/applets/qt_controller.h" 18#include "yuzu/applets/qt_controller.h"
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
deleted file mode 100644
index baa3e55f3..000000000
--- a/src/yuzu/configuration/config.cpp
+++ /dev/null
@@ -1,1306 +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};
39
40const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
41 Qt::Key_7,
42 Qt::Key_8,
43};
44
45const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
46 {
47 Qt::Key_W,
48 Qt::Key_S,
49 Qt::Key_A,
50 Qt::Key_D,
51 },
52 {
53 Qt::Key_I,
54 Qt::Key_K,
55 Qt::Key_J,
56 Qt::Key_L,
57 },
58}};
59
60const std::array<int, 2> Config::default_stick_mod = {
61 Qt::Key_Shift,
62 0,
63};
64
65const std::array<int, 2> Config::default_ringcon_analogs{{
66 Qt::Key_A,
67 Qt::Key_D,
68}};
69
70const std::map<Settings::AntiAliasing, QString> Config::anti_aliasing_texts_map = {
71 {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))},
72 {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))},
73 {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))},
74};
75
76const std::map<Settings::ScalingFilter, QString> Config::scaling_filter_texts_map = {
77 {Settings::ScalingFilter::NearestNeighbor,
78 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))},
79 {Settings::ScalingFilter::Bilinear,
80 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))},
81 {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))},
82 {Settings::ScalingFilter::Gaussian,
83 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))},
84 {Settings::ScalingFilter::ScaleForce,
85 QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))},
86 {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))},
87};
88
89const std::map<Settings::ConsoleMode, QString> Config::use_docked_mode_texts_map = {
90 {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))},
91 {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))},
92};
93
94const std::map<Settings::GpuAccuracy, QString> Config::gpu_accuracy_texts_map = {
95 {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))},
96 {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))},
97 {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))},
98};
99
100const std::map<Settings::RendererBackend, QString> Config::renderer_backend_texts_map = {
101 {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))},
102 {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))},
103 {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))},
104};
105
106const std::map<Settings::ShaderBackend, QString> Config::shader_backend_texts_map = {
107 {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))},
108 {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))},
109 {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))},
110};
111
112// This shouldn't have anything except static initializers (no functions). So
113// QKeySequence(...).toString() is NOT ALLOWED HERE.
114// This must be in alphabetical order according to action name as it must have the same order as
115// UISetting::values.shortcuts, which is alphabetically ordered.
116// clang-format off
117const std::array<UISettings::Shortcut, 23> Config::default_hotkeys{{
118 {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}},
119 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
120 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
121 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
122 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
123 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
124 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
125 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
126 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
127 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
128 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
129 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
130 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
131 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
132 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
133 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
134 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
135 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
136 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
137 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
138 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
139 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}},
140 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
141}};
142// clang-format on
143
144void Config::Initialize(const std::string& config_name) {
145 const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir);
146 const auto config_file = fmt::format("{}.ini", config_name);
147
148 switch (type) {
149 case ConfigType::GlobalConfig:
150 qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file);
151 void(FS::CreateParentDir(qt_config_loc));
152 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
153 QSettings::IniFormat);
154 Reload();
155 break;
156 case ConfigType::PerGameConfig:
157 qt_config_loc =
158 FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file));
159 void(FS::CreateParentDir(qt_config_loc));
160 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
161 QSettings::IniFormat);
162 Reload();
163 break;
164 case ConfigType::InputProfile:
165 qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file);
166 void(FS::CreateParentDir(qt_config_loc));
167 qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
168 QSettings::IniFormat);
169 break;
170 }
171}
172
173bool Config::IsCustomConfig() {
174 return type == ConfigType::PerGameConfig;
175}
176
177void Config::ReadPlayerValue(std::size_t player_index) {
178 const QString player_prefix = [this, player_index] {
179 if (type == ConfigType::InputProfile) {
180 return QString{};
181 } else {
182 return QStringLiteral("player_%1_").arg(player_index);
183 }
184 }();
185
186 auto& player = Settings::values.players.GetValue()[player_index];
187 if (IsCustomConfig()) {
188 const auto profile_name =
189 qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{})
190 .toString()
191 .toStdString();
192 if (profile_name.empty()) {
193 // Use the global input config
194 player = Settings::values.players.GetValue(true)[player_index];
195 return;
196 }
197 player.profile_name = profile_name;
198 }
199
200 if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) {
201 const auto controller = static_cast<Settings::ControllerType>(
202 qt_config
203 ->value(QStringLiteral("%1type").arg(player_prefix),
204 static_cast<u8>(Settings::ControllerType::ProController))
205 .toUInt());
206
207 if (controller == Settings::ControllerType::LeftJoycon ||
208 controller == Settings::ControllerType::RightJoycon) {
209 player.controller_type = controller;
210 }
211 } else {
212 player.connected =
213 ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
214 .toBool();
215
216 player.controller_type = static_cast<Settings::ControllerType>(
217 qt_config
218 ->value(QStringLiteral("%1type").arg(player_prefix),
219 static_cast<u8>(Settings::ControllerType::ProController))
220 .toUInt());
221
222 player.vibration_enabled =
223 qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
224 .toBool();
225
226 player.vibration_strength =
227 qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
228 .toInt();
229
230 player.body_color_left = qt_config
231 ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
232 Settings::JOYCON_BODY_NEON_BLUE)
233 .toUInt();
234 player.body_color_right =
235 qt_config
236 ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
237 Settings::JOYCON_BODY_NEON_RED)
238 .toUInt();
239 player.button_color_left =
240 qt_config
241 ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
242 Settings::JOYCON_BUTTONS_NEON_BLUE)
243 .toUInt();
244 player.button_color_right =
245 qt_config
246 ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
247 Settings::JOYCON_BUTTONS_NEON_RED)
248 .toUInt();
249 }
250
251 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
252 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
253 auto& player_buttons = player.buttons[i];
254
255 player_buttons = qt_config
256 ->value(QStringLiteral("%1").arg(player_prefix) +
257 QString::fromUtf8(Settings::NativeButton::mapping[i]),
258 QString::fromStdString(default_param))
259 .toString()
260 .toStdString();
261 if (player_buttons.empty()) {
262 player_buttons = default_param;
263 }
264 }
265
266 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
267 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
268 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
269 default_analogs[i][3], default_stick_mod[i], 0.5f);
270 auto& player_analogs = player.analogs[i];
271
272 player_analogs = qt_config
273 ->value(QStringLiteral("%1").arg(player_prefix) +
274 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
275 QString::fromStdString(default_param))
276 .toString()
277 .toStdString();
278 if (player_analogs.empty()) {
279 player_analogs = default_param;
280 }
281 }
282
283 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
284 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
285 auto& player_motions = player.motions[i];
286
287 player_motions = qt_config
288 ->value(QStringLiteral("%1").arg(player_prefix) +
289 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
290 QString::fromStdString(default_param))
291 .toString()
292 .toStdString();
293 if (player_motions.empty()) {
294 player_motions = default_param;
295 }
296 }
297}
298
299void Config::ReadDebugValues() {
300 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
301 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
302 auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i];
303
304 debug_pad_buttons = qt_config
305 ->value(QStringLiteral("debug_pad_") +
306 QString::fromUtf8(Settings::NativeButton::mapping[i]),
307 QString::fromStdString(default_param))
308 .toString()
309 .toStdString();
310 if (debug_pad_buttons.empty()) {
311 debug_pad_buttons = default_param;
312 }
313 }
314
315 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
316 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
317 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
318 default_analogs[i][3], default_stick_mod[i], 0.5f);
319 auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
320
321 debug_pad_analogs = qt_config
322 ->value(QStringLiteral("debug_pad_") +
323 QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
324 QString::fromStdString(default_param))
325 .toString()
326 .toStdString();
327 if (debug_pad_analogs.empty()) {
328 debug_pad_analogs = default_param;
329 }
330 }
331}
332
333void Config::ReadTouchscreenValues() {
334 Settings::values.touchscreen.enabled =
335 ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool();
336
337 Settings::values.touchscreen.rotation_angle =
338 ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt();
339 Settings::values.touchscreen.diameter_x =
340 ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt();
341 Settings::values.touchscreen.diameter_y =
342 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
343}
344
345void Config::ReadHidbusValues() {
346 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
347 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
348 auto& ringcon_analogs = Settings::values.ringcon_analogs;
349
350 ringcon_analogs =
351 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
352 .toString()
353 .toStdString();
354 if (ringcon_analogs.empty()) {
355 ringcon_analogs = default_param;
356 }
357}
358
359void Config::ReadAudioValues() {
360 qt_config->beginGroup(QStringLiteral("Audio"));
361
362 ReadCategory(Settings::Category::Audio);
363
364 qt_config->endGroup();
365}
366
367void Config::ReadControlValues() {
368 qt_config->beginGroup(QStringLiteral("Controls"));
369
370 ReadCategory(Settings::Category::Controls);
371
372 Settings::values.players.SetGlobal(!IsCustomConfig());
373 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
374 ReadPlayerValue(p);
375 }
376
377 // Disable docked mode if handheld is selected
378 const auto controller_type = Settings::values.players.GetValue()[0].controller_type;
379 if (controller_type == Settings::ControllerType::Handheld) {
380 Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig());
381 Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld);
382 }
383
384 if (IsCustomConfig()) {
385 qt_config->endGroup();
386 return;
387 }
388 ReadDebugValues();
389 ReadTouchscreenValues();
390 ReadMotionTouchValues();
391 ReadHidbusValues();
392
393 qt_config->endGroup();
394}
395
396void Config::ReadMotionTouchValues() {
397 int num_touch_from_button_maps =
398 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
399
400 if (num_touch_from_button_maps > 0) {
401 const auto append_touch_from_button_map = [this] {
402 Settings::TouchFromButtonMap map;
403 map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
404 .toString()
405 .toStdString();
406 const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
407 map.buttons.reserve(num_touch_maps);
408 for (int i = 0; i < num_touch_maps; i++) {
409 qt_config->setArrayIndex(i);
410 std::string touch_mapping =
411 ReadSetting(QStringLiteral("bind")).toString().toStdString();
412 map.buttons.emplace_back(std::move(touch_mapping));
413 }
414 qt_config->endArray(); // entries
415 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
416 };
417
418 for (int i = 0; i < num_touch_from_button_maps; ++i) {
419 qt_config->setArrayIndex(i);
420 append_touch_from_button_map();
421 }
422 } else {
423 Settings::values.touch_from_button_maps.emplace_back(
424 Settings::TouchFromButtonMap{"default", {}});
425 num_touch_from_button_maps = 1;
426 }
427 qt_config->endArray();
428
429 Settings::values.touch_from_button_map_index = std::clamp(
430 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
431}
432
433void Config::ReadCoreValues() {
434 qt_config->beginGroup(QStringLiteral("Core"));
435
436 ReadCategory(Settings::Category::Core);
437
438 qt_config->endGroup();
439}
440
441void Config::ReadDataStorageValues() {
442 qt_config->beginGroup(QStringLiteral("Data Storage"));
443
444 FS::SetYuzuPath(
445 FS::YuzuPath::NANDDir,
446 qt_config
447 ->value(QStringLiteral("nand_directory"),
448 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)))
449 .toString()
450 .toStdString());
451 FS::SetYuzuPath(
452 FS::YuzuPath::SDMCDir,
453 qt_config
454 ->value(QStringLiteral("sdmc_directory"),
455 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)))
456 .toString()
457 .toStdString());
458 FS::SetYuzuPath(
459 FS::YuzuPath::LoadDir,
460 qt_config
461 ->value(QStringLiteral("load_directory"),
462 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)))
463 .toString()
464 .toStdString());
465 FS::SetYuzuPath(
466 FS::YuzuPath::DumpDir,
467 qt_config
468 ->value(QStringLiteral("dump_directory"),
469 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)))
470 .toString()
471 .toStdString());
472 FS::SetYuzuPath(FS::YuzuPath::TASDir,
473 qt_config
474 ->value(QStringLiteral("tas_directory"),
475 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)))
476 .toString()
477 .toStdString());
478
479 ReadCategory(Settings::Category::DataStorage);
480
481 qt_config->endGroup();
482}
483
484void Config::ReadDebuggingValues() {
485 qt_config->beginGroup(QStringLiteral("Debugging"));
486
487 // Intentionally not using the QT default setting as this is intended to be changed in the ini
488 Settings::values.record_frame_times =
489 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
490
491 ReadCategory(Settings::Category::Debugging);
492 ReadCategory(Settings::Category::DebuggingGraphics);
493
494 qt_config->endGroup();
495}
496
497void Config::ReadServiceValues() {
498 qt_config->beginGroup(QStringLiteral("Services"));
499
500 ReadCategory(Settings::Category::Services);
501
502 qt_config->endGroup();
503}
504
505void Config::ReadDisabledAddOnValues() {
506 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns"));
507
508 for (int i = 0; i < size; ++i) {
509 qt_config->setArrayIndex(i);
510 const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong();
511 std::vector<std::string> out;
512 const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
513 for (int j = 0; j < d_size; ++j) {
514 qt_config->setArrayIndex(j);
515 out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
516 }
517 qt_config->endArray();
518 Settings::values.disabled_addons.insert_or_assign(title_id, out);
519 }
520
521 qt_config->endArray();
522}
523
524void Config::ReadMiscellaneousValues() {
525 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
526
527 ReadCategory(Settings::Category::Miscellaneous);
528
529 qt_config->endGroup();
530}
531
532void Config::ReadPathValues() {
533 qt_config->beginGroup(QStringLiteral("Paths"));
534
535 UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
536 UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
537 UISettings::values.game_dir_deprecated =
538 ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
539 UISettings::values.game_dir_deprecated_deepscan =
540 ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool();
541 const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs"));
542 for (int i = 0; i < gamedirs_size; ++i) {
543 qt_config->setArrayIndex(i);
544 UISettings::GameDir game_dir;
545 game_dir.path = ReadSetting(QStringLiteral("path")).toString();
546 game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool();
547 game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool();
548 UISettings::values.game_dirs.append(game_dir);
549 }
550 qt_config->endArray();
551 // create NAND and SD card directories if empty, these are not removable through the UI,
552 // also carries over old game list settings if present
553 if (UISettings::values.game_dirs.isEmpty()) {
554 UISettings::GameDir game_dir;
555 game_dir.path = QStringLiteral("SDMC");
556 game_dir.expanded = true;
557 UISettings::values.game_dirs.append(game_dir);
558 game_dir.path = QStringLiteral("UserNAND");
559 UISettings::values.game_dirs.append(game_dir);
560 game_dir.path = QStringLiteral("SysNAND");
561 UISettings::values.game_dirs.append(game_dir);
562 if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) {
563 game_dir.path = UISettings::values.game_dir_deprecated;
564 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
565 UISettings::values.game_dirs.append(game_dir);
566 }
567 }
568 UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
569 UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
570
571 qt_config->endGroup();
572}
573
574void Config::ReadCpuValues() {
575 qt_config->beginGroup(QStringLiteral("Cpu"));
576
577 ReadCategory(Settings::Category::Cpu);
578 ReadCategory(Settings::Category::CpuDebug);
579 ReadCategory(Settings::Category::CpuUnsafe);
580
581 qt_config->endGroup();
582}
583
584void Config::ReadRendererValues() {
585 qt_config->beginGroup(QStringLiteral("Renderer"));
586
587 ReadCategory(Settings::Category::Renderer);
588 ReadCategory(Settings::Category::RendererAdvanced);
589 ReadCategory(Settings::Category::RendererDebug);
590
591 qt_config->endGroup();
592}
593
594void Config::ReadScreenshotValues() {
595 qt_config->beginGroup(QStringLiteral("Screenshots"));
596
597 ReadCategory(Settings::Category::Screenshots);
598 FS::SetYuzuPath(
599 FS::YuzuPath::ScreenshotsDir,
600 qt_config
601 ->value(QStringLiteral("screenshot_path"),
602 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)))
603 .toString()
604 .toStdString());
605
606 qt_config->endGroup();
607}
608
609void Config::ReadShortcutValues() {
610 qt_config->beginGroup(QStringLiteral("Shortcuts"));
611
612 for (const auto& [name, group, shortcut] : default_hotkeys) {
613 qt_config->beginGroup(group);
614 qt_config->beginGroup(name);
615 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
616 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
617 // a file dialog in windowed mode
618 UISettings::values.shortcuts.push_back(
619 {name,
620 group,
621 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
622 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
623 .toString(),
624 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
625 qt_config->endGroup();
626 qt_config->endGroup();
627 }
628
629 qt_config->endGroup();
630}
631
632void Config::ReadSystemValues() {
633 qt_config->beginGroup(QStringLiteral("System"));
634
635 ReadCategory(Settings::Category::System);
636 ReadCategory(Settings::Category::SystemAudio);
637
638 qt_config->endGroup();
639}
640
641void Config::ReadUIValues() {
642 qt_config->beginGroup(QStringLiteral("UI"));
643
644 UISettings::values.theme =
645 ReadSetting(
646 QStringLiteral("theme"),
647 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second))
648 .toString();
649
650 ReadUIGamelistValues();
651 ReadUILayoutValues();
652 ReadPathValues();
653 ReadScreenshotValues();
654 ReadShortcutValues();
655 ReadMultiplayerValues();
656
657 ReadCategory(Settings::Category::Ui);
658 ReadCategory(Settings::Category::UiGeneral);
659
660 qt_config->endGroup();
661}
662
663void Config::ReadUIGamelistValues() {
664 qt_config->beginGroup(QStringLiteral("UIGameList"));
665
666 ReadCategory(Settings::Category::UiGameList);
667
668 const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites"));
669 for (int i = 0; i < favorites_size; i++) {
670 qt_config->setArrayIndex(i);
671 UISettings::values.favorited_ids.append(
672 ReadSetting(QStringLiteral("program_id")).toULongLong());
673 }
674 qt_config->endArray();
675
676 qt_config->endGroup();
677}
678
679void Config::ReadUILayoutValues() {
680 qt_config->beginGroup(QStringLiteral("UILayout"));
681
682 UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray();
683 UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray();
684 UISettings::values.renderwindow_geometry =
685 ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray();
686 UISettings::values.gamelist_header_state =
687 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
688 UISettings::values.microprofile_geometry =
689 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
690
691 ReadCategory(Settings::Category::UiLayout);
692
693 qt_config->endGroup();
694}
695
696void Config::ReadWebServiceValues() {
697 qt_config->beginGroup(QStringLiteral("WebService"));
698
699 ReadCategory(Settings::Category::WebService);
700
701 qt_config->endGroup();
702}
703
704void Config::ReadMultiplayerValues() {
705 qt_config->beginGroup(QStringLiteral("Multiplayer"));
706
707 ReadCategory(Settings::Category::Multiplayer);
708
709 // Read ban list back
710 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
711 UISettings::values.multiplayer_ban_list.first.resize(size);
712 for (int i = 0; i < size; ++i) {
713 qt_config->setArrayIndex(i);
714 UISettings::values.multiplayer_ban_list.first[i] =
715 ReadSetting(QStringLiteral("username")).toString().toStdString();
716 }
717 qt_config->endArray();
718 size = qt_config->beginReadArray(QStringLiteral("ip_ban_list"));
719 UISettings::values.multiplayer_ban_list.second.resize(size);
720 for (int i = 0; i < size; ++i) {
721 qt_config->setArrayIndex(i);
722 UISettings::values.multiplayer_ban_list.second[i] =
723 ReadSetting(QStringLiteral("ip")).toString().toStdString();
724 }
725 qt_config->endArray();
726
727 qt_config->endGroup();
728}
729
730void Config::ReadNetworkValues() {
731 qt_config->beginGroup(QString::fromStdString("Services"));
732
733 ReadCategory(Settings::Category::Network);
734
735 qt_config->endGroup();
736}
737
738void Config::ReadValues() {
739 if (global) {
740 ReadDataStorageValues();
741 ReadDebuggingValues();
742 ReadDisabledAddOnValues();
743 ReadNetworkValues();
744 ReadServiceValues();
745 ReadUIValues();
746 ReadWebServiceValues();
747 ReadMiscellaneousValues();
748 }
749 ReadControlValues();
750 ReadCoreValues();
751 ReadCpuValues();
752 ReadRendererValues();
753 ReadAudioValues();
754 ReadSystemValues();
755}
756
757void Config::SavePlayerValue(std::size_t player_index) {
758 const QString player_prefix = [this, player_index] {
759 if (type == ConfigType::InputProfile) {
760 return QString{};
761 } else {
762 return QStringLiteral("player_%1_").arg(player_index);
763 }
764 }();
765
766 const auto& player = Settings::values.players.GetValue()[player_index];
767 if (IsCustomConfig()) {
768 if (player.profile_name.empty()) {
769 // No custom profile selected
770 return;
771 }
772 WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix),
773 QString::fromStdString(player.profile_name), QString{});
774 }
775
776 WriteSetting(QStringLiteral("%1type").arg(player_prefix),
777 static_cast<u8>(player.controller_type),
778 static_cast<u8>(Settings::ControllerType::ProController));
779
780 if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) {
781 WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected,
782 player_index == 0);
783 WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
784 player.vibration_enabled, true);
785 WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
786 player.vibration_strength, 100);
787 WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
788 Settings::JOYCON_BODY_NEON_BLUE);
789 WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
790 player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
791 WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
792 player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
793 WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
794 player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
795 }
796
797 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
798 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
799 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
800 QString::fromStdString(Settings::NativeButton::mapping[i]),
801 QString::fromStdString(player.buttons[i]),
802 QString::fromStdString(default_param));
803 }
804 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
805 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
806 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
807 default_analogs[i][3], default_stick_mod[i], 0.5f);
808 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
809 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
810 QString::fromStdString(player.analogs[i]),
811 QString::fromStdString(default_param));
812 }
813 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
814 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
815 WriteSetting(QStringLiteral("%1").arg(player_prefix) +
816 QString::fromStdString(Settings::NativeMotion::mapping[i]),
817 QString::fromStdString(player.motions[i]),
818 QString::fromStdString(default_param));
819 }
820}
821
822void Config::SaveDebugValues() {
823 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
824 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
825 WriteSetting(QStringLiteral("debug_pad_") +
826 QString::fromStdString(Settings::NativeButton::mapping[i]),
827 QString::fromStdString(Settings::values.debug_pad_buttons[i]),
828 QString::fromStdString(default_param));
829 }
830 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
831 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
832 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
833 default_analogs[i][3], default_stick_mod[i], 0.5f);
834 WriteSetting(QStringLiteral("debug_pad_") +
835 QString::fromStdString(Settings::NativeAnalog::mapping[i]),
836 QString::fromStdString(Settings::values.debug_pad_analogs[i]),
837 QString::fromStdString(default_param));
838 }
839}
840
841void Config::SaveTouchscreenValues() {
842 const auto& touchscreen = Settings::values.touchscreen;
843
844 WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true);
845
846 WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0);
847 WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15);
848 WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
849}
850
851void Config::SaveMotionTouchValues() {
852 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
853 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
854 qt_config->setArrayIndex(static_cast<int>(p));
855 WriteSetting(QStringLiteral("name"),
856 QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
857 QStringLiteral("default"));
858 qt_config->beginWriteArray(QStringLiteral("entries"));
859 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
860 ++q) {
861 qt_config->setArrayIndex(static_cast<int>(q));
862 WriteSetting(
863 QStringLiteral("bind"),
864 QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
865 }
866 qt_config->endArray();
867 }
868 qt_config->endArray();
869}
870
871void Config::SaveHidbusValues() {
872 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
873 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
874 WriteSetting(QStringLiteral("ring_controller"),
875 QString::fromStdString(Settings::values.ringcon_analogs),
876 QString::fromStdString(default_param));
877}
878
879void Config::SaveValues() {
880 if (global) {
881 SaveDataStorageValues();
882 SaveDebuggingValues();
883 SaveDisabledAddOnValues();
884 SaveNetworkValues();
885 SaveUIValues();
886 SaveWebServiceValues();
887 SaveMiscellaneousValues();
888 }
889 SaveControlValues();
890 SaveCoreValues();
891 SaveCpuValues();
892 SaveRendererValues();
893 SaveAudioValues();
894 SaveSystemValues();
895
896 qt_config->sync();
897}
898
899void Config::SaveAudioValues() {
900 qt_config->beginGroup(QStringLiteral("Audio"));
901
902 WriteCategory(Settings::Category::Audio);
903
904 qt_config->endGroup();
905}
906
907void Config::SaveControlValues() {
908 qt_config->beginGroup(QStringLiteral("Controls"));
909
910 WriteCategory(Settings::Category::Controls);
911
912 Settings::values.players.SetGlobal(!IsCustomConfig());
913 for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
914 SavePlayerValue(p);
915 }
916 if (IsCustomConfig()) {
917 qt_config->endGroup();
918 return;
919 }
920 SaveDebugValues();
921 SaveTouchscreenValues();
922 SaveMotionTouchValues();
923 SaveHidbusValues();
924
925 qt_config->endGroup();
926}
927
928void Config::SaveCoreValues() {
929 qt_config->beginGroup(QStringLiteral("Core"));
930
931 WriteCategory(Settings::Category::Core);
932
933 qt_config->endGroup();
934}
935
936void Config::SaveDataStorageValues() {
937 qt_config->beginGroup(QStringLiteral("Data Storage"));
938
939 WriteSetting(QStringLiteral("nand_directory"),
940 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)),
941 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
942 WriteSetting(QStringLiteral("sdmc_directory"),
943 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)),
944 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
945 WriteSetting(QStringLiteral("load_directory"),
946 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)),
947 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
948 WriteSetting(QStringLiteral("dump_directory"),
949 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)),
950 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
951 WriteSetting(QStringLiteral("tas_directory"),
952 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)),
953 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
954
955 WriteCategory(Settings::Category::DataStorage);
956
957 qt_config->endGroup();
958}
959
960void Config::SaveDebuggingValues() {
961 qt_config->beginGroup(QStringLiteral("Debugging"));
962
963 // Intentionally not using the QT default setting as this is intended to be changed in the ini
964 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
965
966 WriteCategory(Settings::Category::Debugging);
967 WriteCategory(Settings::Category::DebuggingGraphics);
968
969 qt_config->endGroup();
970}
971
972void Config::SaveNetworkValues() {
973 qt_config->beginGroup(QStringLiteral("Services"));
974
975 WriteCategory(Settings::Category::Network);
976
977 qt_config->endGroup();
978}
979
980void Config::SaveDisabledAddOnValues() {
981 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns"));
982
983 int i = 0;
984 for (const auto& elem : Settings::values.disabled_addons) {
985 qt_config->setArrayIndex(i);
986 WriteSetting(QStringLiteral("title_id"), QVariant::fromValue<u64>(elem.first), 0);
987 qt_config->beginWriteArray(QStringLiteral("disabled"));
988 for (std::size_t j = 0; j < elem.second.size(); ++j) {
989 qt_config->setArrayIndex(static_cast<int>(j));
990 WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
991 }
992 qt_config->endArray();
993 ++i;
994 }
995
996 qt_config->endArray();
997}
998
999void Config::SaveMiscellaneousValues() {
1000 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
1001
1002 WriteCategory(Settings::Category::Miscellaneous);
1003
1004 qt_config->endGroup();
1005}
1006
1007void Config::SavePathValues() {
1008 qt_config->beginGroup(QStringLiteral("Paths"));
1009
1010 WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
1011 WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
1012 qt_config->beginWriteArray(QStringLiteral("gamedirs"));
1013 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
1014 qt_config->setArrayIndex(i);
1015 const auto& game_dir = UISettings::values.game_dirs[i];
1016 WriteSetting(QStringLiteral("path"), game_dir.path);
1017 WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false);
1018 WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true);
1019 }
1020 qt_config->endArray();
1021 WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
1022 WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
1023
1024 qt_config->endGroup();
1025}
1026
1027void Config::SaveCpuValues() {
1028 qt_config->beginGroup(QStringLiteral("Cpu"));
1029
1030 WriteCategory(Settings::Category::Cpu);
1031 WriteCategory(Settings::Category::CpuDebug);
1032 WriteCategory(Settings::Category::CpuUnsafe);
1033
1034 qt_config->endGroup();
1035}
1036
1037void Config::SaveRendererValues() {
1038 qt_config->beginGroup(QStringLiteral("Renderer"));
1039
1040 WriteCategory(Settings::Category::Renderer);
1041 WriteCategory(Settings::Category::RendererAdvanced);
1042 WriteCategory(Settings::Category::RendererDebug);
1043
1044 qt_config->endGroup();
1045}
1046
1047void Config::SaveScreenshotValues() {
1048 qt_config->beginGroup(QStringLiteral("Screenshots"));
1049
1050 WriteSetting(QStringLiteral("screenshot_path"),
1051 QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)));
1052 WriteCategory(Settings::Category::Screenshots);
1053
1054 qt_config->endGroup();
1055}
1056
1057void Config::SaveShortcutValues() {
1058 qt_config->beginGroup(QStringLiteral("Shortcuts"));
1059
1060 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
1061 // However, their ordering must also be the same.
1062 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
1063 const auto& [name, group, shortcut] = UISettings::values.shortcuts[i];
1064 const auto& default_hotkey = default_hotkeys[i].shortcut;
1065
1066 qt_config->beginGroup(group);
1067 qt_config->beginGroup(name);
1068 WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq);
1069 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1070 default_hotkey.controller_keyseq);
1071 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1072 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1073 qt_config->endGroup();
1074 qt_config->endGroup();
1075 }
1076
1077 qt_config->endGroup();
1078}
1079
1080void Config::SaveSystemValues() {
1081 qt_config->beginGroup(QStringLiteral("System"));
1082
1083 WriteCategory(Settings::Category::System);
1084 WriteCategory(Settings::Category::SystemAudio);
1085
1086 qt_config->endGroup();
1087}
1088
1089void Config::SaveUIValues() {
1090 qt_config->beginGroup(QStringLiteral("UI"));
1091
1092 WriteCategory(Settings::Category::Ui);
1093 WriteCategory(Settings::Category::UiGeneral);
1094
1095 WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
1096 QString::fromUtf8(UISettings::themes[static_cast<size_t>(default_theme)].second));
1097
1098 SaveUIGamelistValues();
1099 SaveUILayoutValues();
1100 SavePathValues();
1101 SaveScreenshotValues();
1102 SaveShortcutValues();
1103 SaveMultiplayerValues();
1104
1105 qt_config->endGroup();
1106}
1107
1108void Config::SaveUIGamelistValues() {
1109 qt_config->beginGroup(QStringLiteral("UIGameList"));
1110
1111 WriteCategory(Settings::Category::UiGameList);
1112
1113 qt_config->beginWriteArray(QStringLiteral("favorites"));
1114 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
1115 qt_config->setArrayIndex(i);
1116 WriteSetting(QStringLiteral("program_id"),
1117 QVariant::fromValue(UISettings::values.favorited_ids[i]));
1118 }
1119 qt_config->endArray();
1120
1121 qt_config->endGroup();
1122}
1123
1124void Config::SaveUILayoutValues() {
1125 qt_config->beginGroup(QStringLiteral("UILayout"));
1126
1127 WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry);
1128 WriteSetting(QStringLiteral("state"), UISettings::values.state);
1129 WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry);
1130 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1131 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1132 UISettings::values.microprofile_geometry);
1133
1134 WriteCategory(Settings::Category::UiLayout);
1135
1136 qt_config->endGroup();
1137}
1138
1139void Config::SaveWebServiceValues() {
1140 qt_config->beginGroup(QStringLiteral("WebService"));
1141
1142 WriteCategory(Settings::Category::WebService);
1143
1144 qt_config->endGroup();
1145}
1146
1147void Config::SaveMultiplayerValues() {
1148 qt_config->beginGroup(QStringLiteral("Multiplayer"));
1149
1150 WriteCategory(Settings::Category::Multiplayer);
1151
1152 // Write ban list
1153 qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
1154 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
1155 qt_config->setArrayIndex(static_cast<int>(i));
1156 WriteSetting(QStringLiteral("username"),
1157 QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i]));
1158 }
1159 qt_config->endArray();
1160 qt_config->beginWriteArray(QStringLiteral("ip_ban_list"));
1161 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
1162 qt_config->setArrayIndex(static_cast<int>(i));
1163 WriteSetting(QStringLiteral("ip"),
1164 QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i]));
1165 }
1166 qt_config->endArray();
1167
1168 qt_config->endGroup();
1169}
1170
1171QVariant Config::ReadSetting(const QString& name) const {
1172 return qt_config->value(name);
1173}
1174
1175QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
1176 QVariant result;
1177 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
1178 result = default_value;
1179 } else {
1180 result = qt_config->value(name, default_value);
1181 }
1182 return result;
1183}
1184
1185void Config::WriteSetting(const QString& name, const QVariant& value) {
1186 qt_config->setValue(name, value);
1187}
1188
1189void Config::WriteSetting(const QString& name, const QVariant& value,
1190 const QVariant& default_value) {
1191 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1192 qt_config->setValue(name, value);
1193}
1194
1195void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value,
1196 bool use_global) {
1197 if (!global) {
1198 qt_config->setValue(name + QStringLiteral("/use_global"), use_global);
1199 }
1200 if (global || !use_global) {
1201 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1202 qt_config->setValue(name, value);
1203 }
1204}
1205
1206void Config::Reload() {
1207 ReadValues();
1208 // To apply default value changes
1209 SaveValues();
1210}
1211
1212void Config::Save() {
1213 SaveValues();
1214}
1215
1216void Config::ReadControlPlayerValue(std::size_t player_index) {
1217 qt_config->beginGroup(QStringLiteral("Controls"));
1218 ReadPlayerValue(player_index);
1219 qt_config->endGroup();
1220}
1221
1222void Config::SaveControlPlayerValue(std::size_t player_index) {
1223 qt_config->beginGroup(QStringLiteral("Controls"));
1224 SavePlayerValue(player_index);
1225 qt_config->endGroup();
1226}
1227
1228void Config::ClearControlPlayerValues() {
1229 qt_config->beginGroup(QStringLiteral("Controls"));
1230 // If key is an empty string, all keys in the current group() are removed.
1231 qt_config->remove(QString{});
1232 qt_config->endGroup();
1233}
1234
1235const std::string& Config::GetConfigFilePath() const {
1236 return qt_config_loc;
1237}
1238
1239static auto FindRelevantList(Settings::Category category) {
1240 auto& map = Settings::values.linkage.by_category;
1241 if (map.contains(category)) {
1242 return Settings::values.linkage.by_category[category];
1243 }
1244 return UISettings::values.linkage.by_category[category];
1245}
1246
1247void Config::ReadCategory(Settings::Category category) {
1248 const auto& settings = FindRelevantList(category);
1249 std::for_each(settings.begin(), settings.end(),
1250 [&](const auto& setting) { ReadSettingGeneric(setting); });
1251}
1252
1253void Config::WriteCategory(Settings::Category category) {
1254 const auto& settings = FindRelevantList(category);
1255 std::for_each(settings.begin(), settings.end(),
1256 [&](const auto& setting) { WriteSettingGeneric(setting); });
1257}
1258
1259void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) {
1260 if (!setting->Save() || (!setting->Switchable() && !global)) {
1261 return;
1262 }
1263 const QString name = QString::fromStdString(setting->GetLabel());
1264 const auto default_value =
1265 QVariant::fromValue<QString>(QString::fromStdString(setting->DefaultToString()));
1266
1267 bool use_global = true;
1268 if (setting->Switchable() && !global) {
1269 use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value<bool>();
1270 setting->SetGlobal(use_global);
1271 }
1272
1273 if (global || !use_global) {
1274 const bool is_default =
1275 qt_config->value(name + QStringLiteral("/default"), true).value<bool>();
1276 if (!is_default) {
1277 setting->LoadString(
1278 qt_config->value(name, default_value).value<QString>().toStdString());
1279 } else {
1280 // Empty string resets the Setting to default
1281 setting->LoadString("");
1282 }
1283 }
1284}
1285
1286void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const {
1287 if (!setting->Save()) {
1288 return;
1289 }
1290 const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString()));
1291 const QVariant default_value =
1292 QVariant::fromValue(QString::fromStdString(setting->DefaultToString()));
1293 const QString label = QString::fromStdString(setting->GetLabel());
1294 if (setting->Switchable()) {
1295 if (!global) {
1296 qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal());
1297 }
1298 if (global || !setting->UsingGlobal()) {
1299 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1300 qt_config->setValue(label, value);
1301 }
1302 } else if (global) {
1303 qt_config->setValue(label + QStringLiteral("/default"), value == default_value);
1304 qt_config->setValue(label, value);
1305 }
1306}
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_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index 81dd51ad3..9b6ef47a7 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) {
38 38
39 std::map<u32, QWidget*> hold; 39 std::map<u32, QWidget*> hold;
40 40
41 auto push = [&](Settings::Category category) { 41 auto push_settings = [&](Settings::Category category) {
42 for (auto* setting : Settings::values.linkage.by_category[category]) { 42 for (auto* setting : Settings::values.linkage.by_category[category]) {
43 settings.push_back(setting); 43 settings.push_back(setting);
44 } 44 }
45 };
46
47 auto push_ui_settings = [&](Settings::Category category) {
45 for (auto* setting : UISettings::values.linkage.by_category[category]) { 48 for (auto* setting : UISettings::values.linkage.by_category[category]) {
46 settings.push_back(setting); 49 settings.push_back(setting);
47 } 50 }
48 }; 51 };
49 52
50 push(Settings::Category::Audio); 53 push_settings(Settings::Category::Audio);
51 push(Settings::Category::SystemAudio); 54 push_settings(Settings::Category::SystemAudio);
55 push_ui_settings(Settings::Category::UiAudio);
52 56
53 for (auto* setting : settings) { 57 for (auto* setting : settings) {
54 auto* widget = builder.BuildWidget(setting, apply_funcs); 58 auto* widget = builder.BuildWidget(setting, apply_funcs);
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_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_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 576f5b571..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"
@@ -322,11 +323,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
322 setFocusPolicy(Qt::ClickFocus); 323 setFocusPolicy(Qt::ClickFocus);
323 324
324 button_map = { 325 button_map = {
325 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, 326 ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
326 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, 327 ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
327 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, 328 ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
328 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, 329 ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
329 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, 330 ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot,
331 ui->buttonSLRight, ui->buttonSRRight,
330 }; 332 };
331 333
332 analog_map_buttons = {{ 334 analog_map_buttons = {{
@@ -1181,10 +1183,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1181 1183
1182 // List of all the widgets that will be hidden by any of the following layouts that need 1184 // List of all the widgets that will be hidden by any of the following layouts that need
1183 // "unhidden" after the controller type changes 1185 // "unhidden" after the controller type changes
1184 const std::array<QWidget*, 11> layout_show = { 1186 const std::array<QWidget*, 14> layout_show = {
1185 ui->buttonShoulderButtonsSLSR, 1187 ui->buttonShoulderButtonsSLSRLeft,
1188 ui->buttonShoulderButtonsSLSRRight,
1186 ui->horizontalSpacerShoulderButtonsWidget, 1189 ui->horizontalSpacerShoulderButtonsWidget,
1187 ui->horizontalSpacerShoulderButtonsWidget2, 1190 ui->horizontalSpacerShoulderButtonsWidget2,
1191 ui->horizontalSpacerShoulderButtonsWidget3,
1192 ui->horizontalSpacerShoulderButtonsWidget4,
1188 ui->buttonShoulderButtonsLeft, 1193 ui->buttonShoulderButtonsLeft,
1189 ui->buttonMiscButtonsMinusScreenshot, 1194 ui->buttonMiscButtonsMinusScreenshot,
1190 ui->bottomLeft, 1195 ui->bottomLeft,
@@ -1202,16 +1207,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1202 std::vector<QWidget*> layout_hidden; 1207 std::vector<QWidget*> layout_hidden;
1203 switch (layout) { 1208 switch (layout) {
1204 case Core::HID::NpadStyleIndex::ProController: 1209 case Core::HID::NpadStyleIndex::ProController:
1205 case Core::HID::NpadStyleIndex::JoyconDual:
1206 case Core::HID::NpadStyleIndex::Handheld: 1210 case Core::HID::NpadStyleIndex::Handheld:
1207 layout_hidden = { 1211 layout_hidden = {
1208 ui->buttonShoulderButtonsSLSR, 1212 ui->buttonShoulderButtonsSLSRLeft,
1213 ui->buttonShoulderButtonsSLSRRight,
1209 ui->horizontalSpacerShoulderButtonsWidget2, 1214 ui->horizontalSpacerShoulderButtonsWidget2,
1215 ui->horizontalSpacerShoulderButtonsWidget4,
1210 }; 1216 };
1211 break; 1217 break;
1212 case Core::HID::NpadStyleIndex::JoyconLeft: 1218 case Core::HID::NpadStyleIndex::JoyconLeft:
1213 layout_hidden = { 1219 layout_hidden = {
1220 ui->buttonShoulderButtonsSLSRRight,
1214 ui->horizontalSpacerShoulderButtonsWidget2, 1221 ui->horizontalSpacerShoulderButtonsWidget2,
1222 ui->horizontalSpacerShoulderButtonsWidget3,
1215 ui->buttonShoulderButtonsRight, 1223 ui->buttonShoulderButtonsRight,
1216 ui->buttonMiscButtonsPlusHome, 1224 ui->buttonMiscButtonsPlusHome,
1217 ui->bottomRight, 1225 ui->bottomRight,
@@ -1219,16 +1227,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
1219 break; 1227 break;
1220 case Core::HID::NpadStyleIndex::JoyconRight: 1228 case Core::HID::NpadStyleIndex::JoyconRight:
1221 layout_hidden = { 1229 layout_hidden = {
1222 ui->horizontalSpacerShoulderButtonsWidget, 1230 ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget,
1223 ui->buttonShoulderButtonsLeft, 1231 ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft,
1224 ui->buttonMiscButtonsMinusScreenshot, 1232 ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft,
1225 ui->bottomLeft,
1226 }; 1233 };
1227 break; 1234 break;
1228 case Core::HID::NpadStyleIndex::GameCube: 1235 case Core::HID::NpadStyleIndex::GameCube:
1229 layout_hidden = { 1236 layout_hidden = {
1230 ui->buttonShoulderButtonsSLSR, 1237 ui->buttonShoulderButtonsSLSRLeft,
1238 ui->buttonShoulderButtonsSLSRRight,
1231 ui->horizontalSpacerShoulderButtonsWidget2, 1239 ui->horizontalSpacerShoulderButtonsWidget2,
1240 ui->horizontalSpacerShoulderButtonsWidget4,
1232 ui->buttonMiscButtonsMinusGroup, 1241 ui->buttonMiscButtonsMinusGroup,
1233 ui->buttonMiscButtonsScreenshotGroup, 1242 ui->buttonMiscButtonsScreenshotGroup,
1234 }; 1243 };
@@ -1389,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
1389 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) {
1390 emulated_controller->SetButtonParam( 1399 emulated_controller->SetButtonParam(
1391 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1400 button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1392 Config::default_buttons[button_id])}); 1401 QtConfig::default_buttons[button_id])});
1393 } 1402 }
1394 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) {
1395 Common::ParamPackage analog_param{}; 1404 Common::ParamPackage analog_param{};
1396 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) {
1397 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 1406 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
1398 Config::default_analogs[analog_id][sub_button_id])}; 1407 QtConfig::default_analogs[analog_id][sub_button_id])};
1399 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); 1408 SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]);
1400 } 1409 }
1401 1410
1402 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( 1411 analog_param.Set("modifier", InputCommon::GenerateKeyboardParam(
1403 Config::default_stick_mod[analog_id])); 1412 QtConfig::default_stick_mod[analog_id]));
1404 emulated_controller->SetStickParam(analog_id, analog_param); 1413 emulated_controller->SetStickParam(analog_id, analog_param);
1405 } 1414 }
1406 1415
1407 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) {
1408 emulated_controller->SetMotionParam( 1417 emulated_controller->SetMotionParam(
1409 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( 1418 motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam(
1410 Config::default_motions[motion_id])}); 1419 QtConfig::default_motions[motion_id])});
1411 } 1420 }
1412 1421
1413 // 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_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 611a79477..5518cccd1 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1208,6 +1208,159 @@
1208 <property name="spacing"> 1208 <property name="spacing">
1209 <number>3</number> 1209 <number>3</number>
1210 </property> 1210 </property>
1211 <item>
1212 <widget class="QWidget" name="buttonShoulderButtonsSLSRLeft" native="true">
1213 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRLeftVerticalLayout">
1214 <property name="spacing">
1215 <number>0</number>
1216 </property>
1217 <property name="leftMargin">
1218 <number>0</number>
1219 </property>
1220 <property name="topMargin">
1221 <number>0</number>
1222 </property>
1223 <property name="rightMargin">
1224 <number>0</number>
1225 </property>
1226 <property name="bottomMargin">
1227 <number>0</number>
1228 </property>
1229 <item alignment="Qt::AlignHCenter">
1230 <widget class="QGroupBox" name="buttonShoulderButtonsSLLeftGroup">
1231 <property name="title">
1232 <string>SL</string>
1233 </property>
1234 <property name="alignment">
1235 <set>Qt::AlignCenter</set>
1236 </property>
1237 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLLeftVerticalLayout">
1238 <property name="spacing">
1239 <number>3</number>
1240 </property>
1241 <property name="leftMargin">
1242 <number>3</number>
1243 </property>
1244 <property name="topMargin">
1245 <number>3</number>
1246 </property>
1247 <property name="rightMargin">
1248 <number>3</number>
1249 </property>
1250 <property name="bottomMargin">
1251 <number>3</number>
1252 </property>
1253 <item>
1254 <widget class="QPushButton" name="buttonSLLeft">
1255 <property name="minimumSize">
1256 <size>
1257 <width>68</width>
1258 <height>0</height>
1259 </size>
1260 </property>
1261 <property name="maximumSize">
1262 <size>
1263 <width>68</width>
1264 <height>16777215</height>
1265 </size>
1266 </property>
1267 <property name="styleSheet">
1268 <string notr="true">min-width: 68px;</string>
1269 </property>
1270 <property name="text">
1271 <string>SL</string>
1272 </property>
1273 </widget>
1274 </item>
1275 </layout>
1276 </widget>
1277 </item>
1278 <item alignment="Qt::AlignHCenter">
1279 <widget class="QGroupBox" name="buttonShoulderButtonsSRLeftGroup">
1280 <property name="title">
1281 <string>SR</string>
1282 </property>
1283 <property name="alignment">
1284 <set>Qt::AlignCenter</set>
1285 </property>
1286 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRLeftVerticalLayout">
1287 <property name="spacing">
1288 <number>3</number>
1289 </property>
1290 <property name="leftMargin">
1291 <number>3</number>
1292 </property>
1293 <property name="topMargin">
1294 <number>3</number>
1295 </property>
1296 <property name="rightMargin">
1297 <number>3</number>
1298 </property>
1299 <property name="bottomMargin">
1300 <number>3</number>
1301 </property>
1302 <item>
1303 <widget class="QPushButton" name="buttonSRLeft">
1304 <property name="minimumSize">
1305 <size>
1306 <width>68</width>
1307 <height>0</height>
1308 </size>
1309 </property>
1310 <property name="maximumSize">
1311 <size>
1312 <width>68</width>
1313 <height>16777215</height>
1314 </size>
1315 </property>
1316 <property name="styleSheet">
1317 <string notr="true">min-width: 68px;</string>
1318 </property>
1319 <property name="text">
1320 <string>SR</string>
1321 </property>
1322 </widget>
1323 </item>
1324 </layout>
1325 </widget>
1326 </item>
1327 </layout>
1328 </widget>
1329 </item>
1330 <item>
1331 <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget4" native="true">
1332 <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget4Layout">
1333 <property name="spacing">
1334 <number>0</number>
1335 </property>
1336 <property name="leftMargin">
1337 <number>0</number>
1338 </property>
1339 <property name="topMargin">
1340 <number>0</number>
1341 </property>
1342 <property name="rightMargin">
1343 <number>0</number>
1344 </property>
1345 <property name="bottomMargin">
1346 <number>0</number>
1347 </property>
1348 <item>
1349 <spacer name="horizontalSpacerShoulderButtons5">
1350 <property name="orientation">
1351 <enum>Qt::Horizontal</enum>
1352 </property>
1353 <property name="sizeHint" stdset="0">
1354 <size>
1355 <width>0</width>
1356 <height>20</height>
1357 </size>
1358 </property>
1359 </spacer>
1360 </item>
1361 </layout>
1362 </widget>
1363 </item>
1211 <item> 1364 <item>
1212 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true"> 1365 <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
1213 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout"> 1366 <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
@@ -1830,125 +1983,125 @@
1830 </layout> 1983 </layout>
1831 </widget> 1984 </widget>
1832 </item> 1985 </item>
1833 <item> 1986 <item>
1834 <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true"> 1987 <widget class="QWidget" name="buttonShoulderButtonsSLSRRight" native="true">
1835 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout"> 1988 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRRightVerticalLayout">
1836 <property name="spacing"> 1989 <property name="spacing">
1837 <number>0</number> 1990 <number>0</number>
1838 </property> 1991 </property>
1839 <property name="leftMargin"> 1992 <property name="leftMargin">
1840 <number>0</number> 1993 <number>0</number>
1841 </property> 1994 </property>
1842 <property name="topMargin"> 1995 <property name="topMargin">
1843 <number>0</number> 1996 <number>0</number>
1844 </property> 1997 </property>
1845 <property name="rightMargin"> 1998 <property name="rightMargin">
1846 <number>0</number> 1999 <number>0</number>
1847 </property> 2000 </property>
1848 <property name="bottomMargin"> 2001 <property name="bottomMargin">
1849 <number>0</number> 2002 <number>0</number>
1850 </property> 2003 </property>
1851 <item alignment="Qt::AlignHCenter"> 2004 <item alignment="Qt::AlignHCenter">
1852 <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup"> 2005 <widget class="QGroupBox" name="buttonShoulderButtonsSLRightGroup">
1853 <property name="title"> 2006 <property name="title">
1854 <string>SL</string> 2007 <string>SL</string>
1855 </property> 2008 </property>
1856 <property name="alignment"> 2009 <property name="alignment">
1857 <set>Qt::AlignCenter</set> 2010 <set>Qt::AlignCenter</set>
1858 </property> 2011 </property>
1859 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> 2012 <layout class="QVBoxLayout" name="buttonShoulderButtonsSLRightVerticalLayout">
1860 <property name="spacing"> 2013 <property name="spacing">
1861 <number>3</number> 2014 <number>3</number>
1862 </property> 2015 </property>
1863 <property name="leftMargin"> 2016 <property name="leftMargin">
1864 <number>3</number> 2017 <number>3</number>
1865 </property> 2018 </property>
1866 <property name="topMargin"> 2019 <property name="topMargin">
1867 <number>3</number> 2020 <number>3</number>
1868 </property> 2021 </property>
1869 <property name="rightMargin"> 2022 <property name="rightMargin">
1870 <number>3</number> 2023 <number>3</number>
1871 </property> 2024 </property>
1872 <property name="bottomMargin"> 2025 <property name="bottomMargin">
1873 <number>3</number> 2026 <number>3</number>
1874 </property> 2027 </property>
1875 <item> 2028 <item>
1876 <widget class="QPushButton" name="buttonSL"> 2029 <widget class="QPushButton" name="buttonSLRight">
1877 <property name="minimumSize"> 2030 <property name="minimumSize">
1878 <size> 2031 <size>
1879 <width>68</width> 2032 <width>68</width>
1880 <height>0</height> 2033 <height>0</height>
1881 </size> 2034 </size>
1882 </property> 2035 </property>
1883 <property name="maximumSize"> 2036 <property name="maximumSize">
1884 <size> 2037 <size>
1885 <width>68</width> 2038 <width>68</width>
1886 <height>16777215</height> 2039 <height>16777215</height>
1887 </size> 2040 </size>
1888 </property> 2041 </property>
1889 <property name="styleSheet"> 2042 <property name="styleSheet">
1890 <string notr="true">min-width: 68px;</string> 2043 <string notr="true">min-width: 68px;</string>
1891 </property> 2044 </property>
1892 <property name="text"> 2045 <property name="text">
1893 <string>SL</string> 2046 <string>SL</string>
1894 </property> 2047 </property>
1895 </widget> 2048 </widget>
1896 </item> 2049 </item>
1897 </layout> 2050 </layout>
1898 </widget> 2051 </widget>
1899 </item> 2052 </item>
1900 <item alignment="Qt::AlignHCenter"> 2053 <item alignment="Qt::AlignHCenter">
1901 <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup"> 2054 <widget class="QGroupBox" name="buttonShoulderButtonsSRRightGroup">
1902 <property name="title"> 2055 <property name="title">
1903 <string>SR</string> 2056 <string>SR</string>
1904 </property> 2057 </property>
1905 <property name="alignment"> 2058 <property name="alignment">
1906 <set>Qt::AlignCenter</set> 2059 <set>Qt::AlignCenter</set>
1907 </property> 2060 </property>
1908 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> 2061 <layout class="QVBoxLayout" name="buttonShoulderButtonsSRRightVerticalLayout">
1909 <property name="spacing"> 2062 <property name="spacing">
1910 <number>3</number> 2063 <number>3</number>
1911 </property> 2064 </property>
1912 <property name="leftMargin"> 2065 <property name="leftMargin">
1913 <number>3</number> 2066 <number>3</number>
1914 </property> 2067 </property>
1915 <property name="topMargin"> 2068 <property name="topMargin">
1916 <number>3</number> 2069 <number>3</number>
1917 </property> 2070 </property>
1918 <property name="rightMargin"> 2071 <property name="rightMargin">
1919 <number>3</number> 2072 <number>3</number>
1920 </property> 2073 </property>
1921 <property name="bottomMargin"> 2074 <property name="bottomMargin">
1922 <number>3</number> 2075 <number>3</number>
1923 </property> 2076 </property>
1924 <item> 2077 <item>
1925 <widget class="QPushButton" name="buttonSR"> 2078 <widget class="QPushButton" name="buttonSRRight">
1926 <property name="minimumSize"> 2079 <property name="minimumSize">
1927 <size> 2080 <size>
1928 <width>68</width> 2081 <width>68</width>
1929 <height>0</height> 2082 <height>0</height>
1930 </size> 2083 </size>
1931 </property> 2084 </property>
1932 <property name="maximumSize"> 2085 <property name="maximumSize">
1933 <size> 2086 <size>
1934 <width>68</width> 2087 <width>68</width>
1935 <height>16777215</height> 2088 <height>16777215</height>
1936 </size> 2089 </size>
1937 </property> 2090 </property>
1938 <property name="styleSheet"> 2091 <property name="styleSheet">
1939 <string notr="true">min-width: 68px;</string> 2092 <string notr="true">min-width: 68px;</string>
1940 </property> 2093 </property>
1941 <property name="text"> 2094 <property name="text">
1942 <string>SR</string> 2095 <string>SR</string>
1943 </property> 2096 </property>
1944 </widget> 2097 </widget>
1945 </item> 2098 </item>
2099 </layout>
2100 </widget>
2101 </item>
1946 </layout> 2102 </layout>
1947 </widget> 2103 </widget>
1948 </item> 2104 </item>
1949 </layout>
1950 </widget>
1951 </item>
1952 </layout> 2105 </layout>
1953 </item> 2106 </item>
1954 <item> 2107 <item>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index a188eef92..550cff9a0 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
297 297
298 // Sideview SL and SR buttons 298 // Sideview SL and SR buttons
299 button_color = colors.slider_button; 299 button_color = colors.slider_button;
300 DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); 300 DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left);
301 DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); 301 DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left);
302 302
303 DrawLeftBody(p, center); 303 DrawLeftBody(p, center);
304 304
@@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center)
353 // SR and SL buttons 353 // SR and SL buttons
354 p.setPen(colors.outline); 354 p.setPen(colors.outline);
355 button_color = colors.slider_button; 355 button_color = colors.slider_button;
356 DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); 356 DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None,
357 DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); 357 4);
358 DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None,
359 4);
358 360
359 // SR and SL text 361 // SR and SL text
360 p.setPen(colors.transparent); 362 p.setPen(colors.transparent);
@@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
428 430
429 // Sideview SL and SR buttons 431 // Sideview SL and SR buttons
430 button_color = colors.slider_button; 432 button_color = colors.slider_button;
431 DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); 433 DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11,
432 DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); 434 Direction::Right);
435 DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11,
436 Direction::Right);
433 437
434 DrawRightBody(p, center); 438 DrawRightBody(p, center);
435 439
@@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center
484 // SR and SL buttons 488 // SR and SL buttons
485 p.setPen(colors.outline); 489 p.setPen(colors.outline);
486 button_color = colors.slider_button; 490 button_color = colors.slider_button;
487 DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); 491 DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None,
488 DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); 492 4.0f);
493 DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None,
494 4.0f);
489 495
490 // SR and SL text 496 // SR and SL text
491 p.setPen(colors.transparent); 497 p.setPen(colors.transparent);
@@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center)
557 DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, 563 DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up,
558 1); 564 1);
559 565
566 // Left SR and SL sideview buttons
567 button_color = colors.slider_button;
568 DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11,
569 Direction::Left);
570 DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left);
571
572 // Right SR and SL sideview buttons
573 button_color = colors.slider_button;
574 DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11,
575 Direction::Right);
576 DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11,
577 Direction::Right);
578
560 DrawDualBody(p, center); 579 DrawDualBody(p, center);
561 580
562 // Right trigger top view 581 // Right trigger top view
@@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) {
1792 p.setBrush(colors.right); 1811 p.setBrush(colors.right);
1793 DrawPolygon(p, qright_joycon_topview); 1812 DrawPolygon(p, qright_joycon_topview);
1794 1813
1795 // Right SR and SL sideview buttons
1796 p.setPen(colors.outline);
1797 p.setBrush(colors.slider_button);
1798 DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1);
1799 DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1);
1800
1801 // Left SR and SL sideview buttons
1802 DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1);
1803 DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1);
1804
1805 // Right Sideview body 1814 // Right Sideview body
1806 p.setBrush(colors.slider); 1815 p.setBrush(colors.slider);
1807 DrawPolygon(p, qright_joycon_slider); 1816 DrawPolygon(p, qright_joycon_slider);
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_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..a7b5def32 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,135 @@ 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());
45 47
46 // Cpu Debug 48 // Cpu Debug
47 49
48 // Cpu Unsafe 50 // 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( 51 INSERT(
63 Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", 52 Settings, cpuopt_unsafe_unfuse_fma,
64 "This option improves speed by eliminating a safety check before every memory read/write " 53 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."); 54 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", 55 "CPUs without native FMA support."));
67 "This option improves speed by relying only on the semantics of cmpxchg to ensure " 56 INSERT(
57 Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"),
58 tr("This option improves the speed of some approximate floating-point functions by using "
59 "less accurate native approximations."));
60 INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr,
61 tr("Faster ASIMD instructions (32 bits only)"),
62 tr("This option improves the speed of 32 bits ASIMD floating-point functions by running "
63 "with incorrect rounding modes."));
64 INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"),
65 tr("This option improves speed by removing NaN checking. Please note this also reduces "
66 "accuracy of certain floating-point instructions."));
67 INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"),
68 tr("This option improves speed by eliminating a safety check before every memory "
69 "read/write "
70 "in guest. Disabling it may allow a game to read/write the emulator's memory."));
71 INSERT(
72 Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"),
73 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 " 74 "safety of exclusive access instructions. Please note this may result in deadlocks and "
69 "other race conditions."); 75 "other race conditions."));
70 76
71 // Renderer 77 // Renderer
72 INSERT(Settings, renderer_backend, "API:", ""); 78 INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral());
73 INSERT(Settings, vulkan_device, "Device:", ""); 79 INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral());
74 INSERT(Settings, shader_backend, "Shader Backend:", ""); 80 INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral());
75 INSERT(Settings, resolution_setup, "Resolution:", ""); 81 INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral());
76 INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); 82 INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral());
77 INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); 83 INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral());
78 INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); 84 INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral());
79 INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); 85 INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral());
80 INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); 86 INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral());
81 INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); 87 INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral());
82 INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); 88 INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"),
83 INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); 89 QStringLiteral());
84 INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); 90 INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral());
85 INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); 91 INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral());
86 INSERT(Settings, vsync_mode, "VSync Mode:", 92 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 " 93 INSERT(
94 Settings, vsync_mode, tr("VSync Mode:"),
95 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 " 96 "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 " 97 "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 " 98 "frames.\nImmediate (no synchronization) just presents whatever is available and can "
91 "exhibit tearing."); 99 "exhibit tearing."));
92 INSERT(Settings, bg_red, "", ""); 100 INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral());
93 INSERT(Settings, bg_green, "", ""); 101 INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral());
94 INSERT(Settings, bg_blue, "", ""); 102 INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral());
95 103
96 // Renderer (Advanced Graphics) 104 // Renderer (Advanced Graphics)
97 INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); 105 INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"),
98 INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", 106 QStringLiteral());
99 "Runs work in the background while waiting for graphics commands to keep the GPU from " 107 INSERT(
100 "lowering its clock speed."); 108 Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"),
101 INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); 109 tr("Runs work in the background while waiting for graphics commands to keep the GPU from "
102 INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); 110 "lowering its clock speed."));
103 INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", 111 INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral());
104 "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " 112 INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral());
105 "is experimental."); 113 INSERT(
106 INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", 114 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 " 115 tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature "
108 "native resolution."); 116 "is experimental."));
109 INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", 117 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 " 118 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 " 119 "native resolution."));
112 "files internally."); 120 INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"),
113 INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", 121 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 " 122 "time significantly in cases where the Vulkan driver does not store pipeline cache "
123 "files internally."));
124 INSERT(
125 Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"),
126 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 " 127 "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled "
116 "on all other drivers."); 128 "on all other drivers."));
117 INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", 129 INSERT(
118 "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " 130 Settings, use_reactive_flushing, tr("Enable Reactive Flushing"),
119 "syncing."); 131 tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory "
120 INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", 132 "syncing."));
121 "Run the game at normal speed during video playback, even when the framerate is " 133 INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"),
122 "unlocked."); 134 tr("Run the game at normal speed during video playback, even when the framerate is "
123 INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", 135 "unlocked."));
124 "Improves rendering of transparency effects in specific games."); 136 INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"),
137 tr("Improves rendering of transparency effects in specific games."));
125 138
126 // Renderer (Debug) 139 // Renderer (Debug)
127 140
128 // System 141 // System
129 INSERT(Settings, rng_seed, "RNG Seed", ""); 142 INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral());
130 INSERT(Settings, rng_seed_enabled, "", ""); 143 INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral());
131 INSERT(Settings, device_name, "Device Name", ""); 144 INSERT(Settings, device_name, tr("Device Name"), QStringLiteral());
132 INSERT(Settings, custom_rtc, "Custom RTC", ""); 145 INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral());
133 INSERT(Settings, custom_rtc_enabled, "", ""); 146 INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral());
134 INSERT(Settings, language_index, 147 INSERT(Settings, language_index, tr("Language:"),
135 "Language:", "Note: this can be overridden when region setting is auto-select"); 148 tr("Note: this can be overridden when region setting is auto-select"));
136 INSERT(Settings, region_index, "Region:", ""); 149 INSERT(Settings, region_index, tr("Region:"), QStringLiteral());
137 INSERT(Settings, time_zone_index, "Time Zone:", ""); 150 INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral());
138 INSERT(Settings, sound_index, "Sound Output Mode:", ""); 151 INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral());
139 INSERT(Settings, use_docked_mode, "Console Mode:", ""); 152 INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral());
140 INSERT(Settings, current_user, "", ""); 153 INSERT(Settings, current_user, QStringLiteral(), QStringLiteral());
141 154
142 // Controls 155 // Controls
143 156
@@ -154,11 +167,14 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
154 // Ui 167 // Ui
155 168
156 // Ui General 169 // Ui General
157 INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); 170 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", ""); 171 INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"),
159 INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); 172 QStringLiteral());
160 INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); 173 INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"),
161 INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); 174 QStringLiteral());
175 INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral());
176 INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"),
177 QStringLiteral());
162 178
163 // Ui Debugging 179 // Ui Debugging
164 180
@@ -178,140 +194,141 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
178 return parent->tr(text, context); 194 return parent->tr(text, context);
179 }; 195 };
180 196
181#define PAIR(ENUM, VALUE, TRANSLATION) \ 197#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 198
186 // Intentionally skipping VSyncMode to let the UI fill that one out 199 // Intentionally skipping VSyncMode to let the UI fill that one out
187 200
188 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(), 201 translations->insert({Settings::EnumMetadata<Settings::AstcDecodeMode>::Index(),
189 { 202 {
190 PAIR(AstcDecodeMode, Cpu, "CPU"), 203 PAIR(AstcDecodeMode, Cpu, tr("CPU")),
191 PAIR(AstcDecodeMode, Gpu, "GPU"), 204 PAIR(AstcDecodeMode, Gpu, tr("GPU")),
192 PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), 205 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 }}); 206 }});
207 translations->insert(
208 {Settings::EnumMetadata<Settings::AstcRecompression>::Index(),
209 {
210 PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")),
211 PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
212 PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
213 }});
200 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), 214 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
201 { 215 {
202#ifdef HAS_OPENGL 216#ifdef HAS_OPENGL
203 PAIR(RendererBackend, OpenGL, "OpenGL"), 217 PAIR(RendererBackend, OpenGL, tr("OpenGL")),
204#endif 218#endif
205 PAIR(RendererBackend, Vulkan, "Vulkan"), 219 PAIR(RendererBackend, Vulkan, tr("Vulkan")),
206 PAIR(RendererBackend, Null, "Null"), 220 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 }}); 221 }});
222 translations->insert(
223 {Settings::EnumMetadata<Settings::ShaderBackend>::Index(),
224 {
225 PAIR(ShaderBackend, Glsl, tr("GLSL")),
226 PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")),
227 PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")),
228 }});
214 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(), 229 translations->insert({Settings::EnumMetadata<Settings::GpuAccuracy>::Index(),
215 { 230 {
216 PAIR(GpuAccuracy, Normal, "Normal"), 231 PAIR(GpuAccuracy, Normal, tr("Normal")),
217 PAIR(GpuAccuracy, High, "High"), 232 PAIR(GpuAccuracy, High, tr("High")),
218 PAIR(GpuAccuracy, Extreme, "Extreme"), 233 PAIR(GpuAccuracy, Extreme, tr("Extreme")),
219 }});
220 translations->insert({Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
221 {
222 PAIR(CpuAccuracy, Auto, "Auto"),
223 PAIR(CpuAccuracy, Accurate, "Accurate"),
224 PAIR(CpuAccuracy, Unsafe, "Unsafe"),
225 PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"),
226 }}); 234 }});
235 translations->insert(
236 {Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
237 {
238 PAIR(CpuAccuracy, Auto, tr("Auto")),
239 PAIR(CpuAccuracy, Accurate, tr("Accurate")),
240 PAIR(CpuAccuracy, Unsafe, tr("Unsafe")),
241 PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")),
242 }});
227 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(), 243 translations->insert({Settings::EnumMetadata<Settings::FullscreenMode>::Index(),
228 { 244 {
229 PAIR(FullscreenMode, Borderless, "Borderless Windowed"), 245 PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")),
230 PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), 246 PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")),
231 }}); 247 }});
232 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(), 248 translations->insert({Settings::EnumMetadata<Settings::NvdecEmulation>::Index(),
233 { 249 {
234 PAIR(NvdecEmulation, Off, "No Video Output"), 250 PAIR(NvdecEmulation, Off, tr("No Video Output")),
235 PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), 251 PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")),
236 PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), 252 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 }}); 253 }});
254 translations->insert(
255 {Settings::EnumMetadata<Settings::ResolutionSetup>::Index(),
256 {
257 PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")),
258 PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")),
259 PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")),
260 PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")),
261 PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")),
262 PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")),
263 PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")),
264 PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")),
265 PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")),
266 PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")),
267 PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")),
268 }});
252 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(), 269 translations->insert({Settings::EnumMetadata<Settings::ScalingFilter>::Index(),
253 { 270 {
254 PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), 271 PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")),
255 PAIR(ScalingFilter, Bilinear, "Bilinear"), 272 PAIR(ScalingFilter, Bilinear, tr("Bilinear")),
256 PAIR(ScalingFilter, Bicubic, "Bicubic"), 273 PAIR(ScalingFilter, Bicubic, tr("Bicubic")),
257 PAIR(ScalingFilter, Gaussian, "Gaussian"), 274 PAIR(ScalingFilter, Gaussian, tr("Gaussian")),
258 PAIR(ScalingFilter, ScaleForce, "ScaleForce"), 275 PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")),
259 PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), 276 PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")),
260 }}); 277 }});
261 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(), 278 translations->insert({Settings::EnumMetadata<Settings::AntiAliasing>::Index(),
262 { 279 {
263 PAIR(AntiAliasing, None, "None"), 280 PAIR(AntiAliasing, None, tr("None")),
264 PAIR(AntiAliasing, Fxaa, "FXAA"), 281 PAIR(AntiAliasing, Fxaa, tr("FXAA")),
265 PAIR(AntiAliasing, Smaa, "SMAA"), 282 PAIR(AntiAliasing, Smaa, tr("SMAA")),
266 }}); 283 }});
267 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(), 284 translations->insert({Settings::EnumMetadata<Settings::AspectRatio>::Index(),
268 { 285 {
269 PAIR(AspectRatio, R16_9, "Default (16:9)"), 286 PAIR(AspectRatio, R16_9, tr("Default (16:9)")),
270 PAIR(AspectRatio, R4_3, "Force 4:3"), 287 PAIR(AspectRatio, R4_3, tr("Force 4:3")),
271 PAIR(AspectRatio, R21_9, "Force 21:9"), 288 PAIR(AspectRatio, R21_9, tr("Force 21:9")),
272 PAIR(AspectRatio, R16_10, "Force 16:10"), 289 PAIR(AspectRatio, R16_10, tr("Force 16:10")),
273 PAIR(AspectRatio, Stretch, "Stretch to Window"), 290 PAIR(AspectRatio, Stretch, tr("Stretch to Window")),
274 }}); 291 }});
275 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(), 292 translations->insert({Settings::EnumMetadata<Settings::AnisotropyMode>::Index(),
276 { 293 {
277 PAIR(AnisotropyMode, Automatic, "Automatic"), 294 PAIR(AnisotropyMode, Automatic, tr("Automatic")),
278 PAIR(AnisotropyMode, Default, "Default"), 295 PAIR(AnisotropyMode, Default, tr("Default")),
279 PAIR(AnisotropyMode, X2, "2x"), 296 PAIR(AnisotropyMode, X2, tr("2x")),
280 PAIR(AnisotropyMode, X4, "4x"), 297 PAIR(AnisotropyMode, X4, tr("4x")),
281 PAIR(AnisotropyMode, X8, "8x"), 298 PAIR(AnisotropyMode, X8, tr("8x")),
282 PAIR(AnisotropyMode, X16, "16x"), 299 PAIR(AnisotropyMode, X16, tr("16x")),
283 }}); 300 }});
284 translations->insert( 301 translations->insert(
285 {Settings::EnumMetadata<Settings::Language>::Index(), 302 {Settings::EnumMetadata<Settings::Language>::Index(),
286 { 303 {
287 PAIR(Language, Japanese, "Japanese (日本語)"), 304 PAIR(Language, Japanese, tr("Japanese (日本語)")),
288 PAIR(Language, EnglishAmerican, "American English"), 305 PAIR(Language, EnglishAmerican, tr("American English")),
289 PAIR(Language, French, "French (français)"), 306 PAIR(Language, French, tr("French (français)")),
290 PAIR(Language, German, "German (Deutsch)"), 307 PAIR(Language, German, tr("German (Deutsch)")),
291 PAIR(Language, Italian, "Italian (italiano)"), 308 PAIR(Language, Italian, tr("Italian (italiano)")),
292 PAIR(Language, Spanish, "Spanish (español)"), 309 PAIR(Language, Spanish, tr("Spanish (español)")),
293 PAIR(Language, Chinese, "Chinese"), 310 PAIR(Language, Chinese, tr("Chinese")),
294 PAIR(Language, Korean, "Korean (한국어)"), 311 PAIR(Language, Korean, tr("Korean (한국어)")),
295 PAIR(Language, Dutch, "Dutch (Nederlands)"), 312 PAIR(Language, Dutch, tr("Dutch (Nederlands)")),
296 PAIR(Language, Portuguese, "Portuguese (português)"), 313 PAIR(Language, Portuguese, tr("Portuguese (português)")),
297 PAIR(Language, Russian, "Russian (Русский)"), 314 PAIR(Language, Russian, tr("Russian (Русский)")),
298 PAIR(Language, Taiwanese, "Taiwanese"), 315 PAIR(Language, Taiwanese, tr("Taiwanese")),
299 PAIR(Language, EnglishBritish, "British English"), 316 PAIR(Language, EnglishBritish, tr("British English")),
300 PAIR(Language, FrenchCanadian, "Canadian French"), 317 PAIR(Language, FrenchCanadian, tr("Canadian French")),
301 PAIR(Language, SpanishLatin, "Latin American Spanish"), 318 PAIR(Language, SpanishLatin, tr("Latin American Spanish")),
302 PAIR(Language, ChineseSimplified, "Simplified Chinese"), 319 PAIR(Language, ChineseSimplified, tr("Simplified Chinese")),
303 PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), 320 PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")),
304 PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), 321 PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")),
305 }}); 322 }});
306 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(), 323 translations->insert({Settings::EnumMetadata<Settings::Region>::Index(),
307 { 324 {
308 PAIR(Region, Japan, "Japan"), 325 PAIR(Region, Japan, tr("Japan")),
309 PAIR(Region, Usa, "USA"), 326 PAIR(Region, Usa, tr("USA")),
310 PAIR(Region, Europe, "Europe"), 327 PAIR(Region, Europe, tr("Europe")),
311 PAIR(Region, Australia, "Australia"), 328 PAIR(Region, Australia, tr("Australia")),
312 PAIR(Region, China, "China"), 329 PAIR(Region, China, tr("China")),
313 PAIR(Region, Korea, "Korea"), 330 PAIR(Region, Korea, tr("Korea")),
314 PAIR(Region, Taiwan, "Taiwan"), 331 PAIR(Region, Taiwan, tr("Taiwan")),
315 }}); 332 }});
316 translations->insert( 333 translations->insert(
317 {Settings::EnumMetadata<Settings::TimeZone>::Index(), 334 {Settings::EnumMetadata<Settings::TimeZone>::Index(),
@@ -323,72 +340,74 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
323 {static_cast<u32>(Settings::TimeZone::Default), 340 {static_cast<u32>(Settings::TimeZone::Default),
324 tr("Default (%1)", "Default time zone") 341 tr("Default (%1)", "Default time zone")
325 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, 342 .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))},
326 PAIR(TimeZone, Cet, "CET"), 343 PAIR(TimeZone, Cet, tr("CET")),
327 PAIR(TimeZone, Cst6Cdt, "CST6CDT"), 344 PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")),
328 PAIR(TimeZone, Cuba, "Cuba"), 345 PAIR(TimeZone, Cuba, tr("Cuba")),
329 PAIR(TimeZone, Eet, "EET"), 346 PAIR(TimeZone, Eet, tr("EET")),
330 PAIR(TimeZone, Egypt, "Egypt"), 347 PAIR(TimeZone, Egypt, tr("Egypt")),
331 PAIR(TimeZone, Eire, "Eire"), 348 PAIR(TimeZone, Eire, tr("Eire")),
332 PAIR(TimeZone, Est, "EST"), 349 PAIR(TimeZone, Est, tr("EST")),
333 PAIR(TimeZone, Est5Edt, "EST5EDT"), 350 PAIR(TimeZone, Est5Edt, tr("EST5EDT")),
334 PAIR(TimeZone, Gb, "GB"), 351 PAIR(TimeZone, Gb, tr("GB")),
335 PAIR(TimeZone, GbEire, "GB-Eire"), 352 PAIR(TimeZone, GbEire, tr("GB-Eire")),
336 PAIR(TimeZone, Gmt, "GMT"), 353 PAIR(TimeZone, Gmt, tr("GMT")),
337 PAIR(TimeZone, GmtPlusZero, "GMT+0"), 354 PAIR(TimeZone, GmtPlusZero, tr("GMT+0")),
338 PAIR(TimeZone, GmtMinusZero, "GMT-0"), 355 PAIR(TimeZone, GmtMinusZero, tr("GMT-0")),
339 PAIR(TimeZone, GmtZero, "GMT0"), 356 PAIR(TimeZone, GmtZero, tr("GMT0")),
340 PAIR(TimeZone, Greenwich, "Greenwich"), 357 PAIR(TimeZone, Greenwich, tr("Greenwich")),
341 PAIR(TimeZone, Hongkong, "Hongkong"), 358 PAIR(TimeZone, Hongkong, tr("Hongkong")),
342 PAIR(TimeZone, Hst, "HST"), 359 PAIR(TimeZone, Hst, tr("HST")),
343 PAIR(TimeZone, Iceland, "Iceland"), 360 PAIR(TimeZone, Iceland, tr("Iceland")),
344 PAIR(TimeZone, Iran, "Iran"), 361 PAIR(TimeZone, Iran, tr("Iran")),
345 PAIR(TimeZone, Israel, "Israel"), 362 PAIR(TimeZone, Israel, tr("Israel")),
346 PAIR(TimeZone, Jamaica, "Jamaica"), 363 PAIR(TimeZone, Jamaica, tr("Jamaica")),
347 PAIR(TimeZone, Japan, "Japan"), 364 PAIR(TimeZone, Japan, tr("Japan")),
348 PAIR(TimeZone, Kwajalein, "Kwajalein"), 365 PAIR(TimeZone, Kwajalein, tr("Kwajalein")),
349 PAIR(TimeZone, Libya, "Libya"), 366 PAIR(TimeZone, Libya, tr("Libya")),
350 PAIR(TimeZone, Met, "MET"), 367 PAIR(TimeZone, Met, tr("MET")),
351 PAIR(TimeZone, Mst, "MST"), 368 PAIR(TimeZone, Mst, tr("MST")),
352 PAIR(TimeZone, Mst7Mdt, "MST7MDT"), 369 PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")),
353 PAIR(TimeZone, Navajo, "Navajo"), 370 PAIR(TimeZone, Navajo, tr("Navajo")),
354 PAIR(TimeZone, Nz, "NZ"), 371 PAIR(TimeZone, Nz, tr("NZ")),
355 PAIR(TimeZone, NzChat, "NZ-CHAT"), 372 PAIR(TimeZone, NzChat, tr("NZ-CHAT")),
356 PAIR(TimeZone, Poland, "Poland"), 373 PAIR(TimeZone, Poland, tr("Poland")),
357 PAIR(TimeZone, Portugal, "Portugal"), 374 PAIR(TimeZone, Portugal, tr("Portugal")),
358 PAIR(TimeZone, Prc, "PRC"), 375 PAIR(TimeZone, Prc, tr("PRC")),
359 PAIR(TimeZone, Pst8Pdt, "PST8PDT"), 376 PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")),
360 PAIR(TimeZone, Roc, "ROC"), 377 PAIR(TimeZone, Roc, tr("ROC")),
361 PAIR(TimeZone, Rok, "ROK"), 378 PAIR(TimeZone, Rok, tr("ROK")),
362 PAIR(TimeZone, Singapore, "Singapore"), 379 PAIR(TimeZone, Singapore, tr("Singapore")),
363 PAIR(TimeZone, Turkey, "Turkey"), 380 PAIR(TimeZone, Turkey, tr("Turkey")),
364 PAIR(TimeZone, Uct, "UCT"), 381 PAIR(TimeZone, Uct, tr("UCT")),
365 PAIR(TimeZone, Universal, "Universal"), 382 PAIR(TimeZone, Universal, tr("Universal")),
366 PAIR(TimeZone, Utc, "UTC"), 383 PAIR(TimeZone, Utc, tr("UTC")),
367 PAIR(TimeZone, WSu, "W-SU"), 384 PAIR(TimeZone, WSu, tr("W-SU")),
368 PAIR(TimeZone, Wet, "WET"), 385 PAIR(TimeZone, Wet, tr("WET")),
369 PAIR(TimeZone, Zulu, "Zulu"), 386 PAIR(TimeZone, Zulu, tr("Zulu")),
370 }}); 387 }});
371 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(), 388 translations->insert({Settings::EnumMetadata<Settings::AudioMode>::Index(),
372 { 389 {
373 PAIR(AudioMode, Mono, "Mono"), 390 PAIR(AudioMode, Mono, tr("Mono")),
374 PAIR(AudioMode, Stereo, "Stereo"), 391 PAIR(AudioMode, Stereo, tr("Stereo")),
375 PAIR(AudioMode, Surround, "Surround"), 392 PAIR(AudioMode, Surround, tr("Surround")),
376 }}); 393 }});
377 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(), 394 translations->insert({Settings::EnumMetadata<Settings::MemoryLayout>::Index(),
378 { 395 {
379 PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), 396 PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")),
380 PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), 397 PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")),
381 PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), 398 PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")),
399 }});
400 translations->insert({Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
401 {
402 PAIR(ConsoleMode, Docked, tr("Docked")),
403 PAIR(ConsoleMode, Handheld, tr("Handheld")),
382 }}); 404 }});
383 translations->insert(
384 {Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
385 {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
386 translations->insert( 405 translations->insert(
387 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(), 406 {Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
388 { 407 {
389 PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), 408 PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")),
390 PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), 409 PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")),
391 PAIR(ConfirmStop, Ask_Never, "Never ask"), 410 PAIR(ConfirmStop, Ask_Never, tr("Never ask")),
392 }}); 411 }});
393 412
394#undef PAIR 413#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 7e7d8e252..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(
@@ -567,9 +567,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
567 QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); 567 QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity"));
568 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); 568 QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard"));
569 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); 569 QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
570// TODO: Implement shortcut creation for macOS
571#if !defined(__APPLE__)
570 QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); 572 QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut"));
571 QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); 573 QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop"));
572#ifndef WIN32
573 QAction* create_applications_menu_shortcut = 574 QAction* create_applications_menu_shortcut =
574 shortcut_menu->addAction(tr("Add to Applications Menu")); 575 shortcut_menu->addAction(tr("Add to Applications Menu"));
575#endif 576#endif
@@ -647,10 +648,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
647 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { 648 connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() {
648 emit NavigateToGamedbEntryRequested(program_id, compatibility_list); 649 emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
649 }); 650 });
651// TODO: Implement shortcut creation for macOS
652#if !defined(__APPLE__)
650 connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { 653 connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() {
651 emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); 654 emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop);
652 }); 655 });
653#ifndef WIN32
654 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { 656 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() {
655 emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); 657 emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications);
656 }); 658 });
@@ -725,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) {
725 }); 727 });
726 728
727 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { 729 connect(open_directory_location, &QAction::triggered, [this, game_dir_index] {
728 emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); 730 emit OpenDirectory(
731 QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path));
729 }); 732 });
730} 733}
731 734
@@ -867,7 +870,7 @@ const QStringList GameList::supported_file_extensions = {
867 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; 870 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
868 871
869void GameList::RefreshGameDirectory() { 872void GameList::RefreshGameDirectory() {
870 if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { 873 if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) {
871 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.");
872 PopulateAsync(UISettings::values.game_dirs); 875 PopulateAsync(UISettings::values.game_dirs);
873 } 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 d2a054eaa..dcf68460a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11#include "core/loader/nca.h" 11#include "core/loader/nca.h"
12#include "core/tools/renderdoc.h" 12#include "core/tools/renderdoc.h"
13
13#ifdef __APPLE__ 14#ifdef __APPLE__
14#include <unistd.h> // for chdir 15#include <unistd.h> // for chdir
15#endif 16#endif
@@ -46,6 +47,7 @@
46#include "core/hle/service/am/applet_ae.h" 47#include "core/hle/service/am/applet_ae.h"
47#include "core/hle/service/am/applet_oe.h" 48#include "core/hle/service/am/applet_oe.h"
48#include "core/hle/service/am/applets/applets.h" 49#include "core/hle/service/am/applets/applets.h"
50#include "core/hle/service/set/set_sys.h"
49#include "yuzu/multiplayer/state.h" 51#include "yuzu/multiplayer/state.h"
50#include "yuzu/util/controller_navigation.h" 52#include "yuzu/util/controller_navigation.h"
51 53
@@ -127,6 +129,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
127#include "core/loader/loader.h" 129#include "core/loader/loader.h"
128#include "core/perf_stats.h" 130#include "core/perf_stats.h"
129#include "core/telemetry_session.h" 131#include "core/telemetry_session.h"
132#include "frontend_common/config.h"
130#include "input_common/drivers/tas_input.h" 133#include "input_common/drivers/tas_input.h"
131#include "input_common/drivers/virtual_amiibo.h" 134#include "input_common/drivers/virtual_amiibo.h"
132#include "input_common/main.h" 135#include "input_common/main.h"
@@ -139,9 +142,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
139#include "yuzu/bootmanager.h" 142#include "yuzu/bootmanager.h"
140#include "yuzu/compatdb.h" 143#include "yuzu/compatdb.h"
141#include "yuzu/compatibility_list.h" 144#include "yuzu/compatibility_list.h"
142#include "yuzu/configuration/config.h"
143#include "yuzu/configuration/configure_dialog.h" 145#include "yuzu/configuration/configure_dialog.h"
144#include "yuzu/configuration/configure_input_per_game.h" 146#include "yuzu/configuration/configure_input_per_game.h"
147#include "yuzu/configuration/qt_config.h"
145#include "yuzu/debugger/console.h" 148#include "yuzu/debugger/console.h"
146#include "yuzu/debugger/controller.h" 149#include "yuzu/debugger/controller.h"
147#include "yuzu/debugger/profiler.h" 150#include "yuzu/debugger/profiler.h"
@@ -310,7 +313,7 @@ bool GMainWindow::CheckDarkMode() {
310#endif // __unix__ 313#endif // __unix__
311} 314}
312 315
313GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan) 316GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan)
314 : 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>()},
315 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)}, 318 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, config{std::move(config_)},
316 vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 319 vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
@@ -675,7 +678,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers(
675 // Don't forget to apply settings. 678 // Don't forget to apply settings.
676 system->HIDCore().DisableAllControllerConfiguration(); 679 system->HIDCore().DisableAllControllerConfiguration();
677 system->ApplySettings(); 680 system->ApplySettings();
678 config->Save(); 681 config->SaveAllValues();
679 682
680 UpdateStatusButtons(); 683 UpdateStatusButtons();
681 684
@@ -1046,7 +1049,12 @@ void GMainWindow::InitializeWidgets() {
1046 statusBar()->addPermanentWidget(label); 1049 statusBar()->addPermanentWidget(label);
1047 } 1050 }
1048 1051
1049 // TODO (flTobi): Add the widget when multiplayer is fully implemented 1052 firmware_label = new QLabel();
1053 firmware_label->setObjectName(QStringLiteral("FirmwareLabel"));
1054 firmware_label->setVisible(false);
1055 firmware_label->setFocusPolicy(Qt::NoFocus);
1056 statusBar()->addPermanentWidget(firmware_label);
1057
1050 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); 1058 statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0);
1051 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); 1059 statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0);
1052 1060
@@ -1064,12 +1072,6 @@ void GMainWindow::InitializeWidgets() {
1064 volume_slider->setObjectName(QStringLiteral("volume_slider")); 1072 volume_slider->setObjectName(QStringLiteral("volume_slider"));
1065 volume_slider->setMaximum(200); 1073 volume_slider->setMaximum(200);
1066 volume_slider->setPageStep(5); 1074 volume_slider->setPageStep(5);
1067 connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
1068 Settings::values.audio_muted = false;
1069 const auto volume = static_cast<u8>(percentage);
1070 Settings::values.volume.SetValue(volume);
1071 UpdateVolumeUI();
1072 });
1073 volume_popup->layout()->addWidget(volume_slider); 1075 volume_popup->layout()->addWidget(volume_slider);
1074 1076
1075 volume_button = new VolumeButton(); 1077 volume_button = new VolumeButton();
@@ -1077,6 +1079,12 @@ void GMainWindow::InitializeWidgets() {
1077 volume_button->setFocusPolicy(Qt::NoFocus); 1079 volume_button->setFocusPolicy(Qt::NoFocus);
1078 volume_button->setCheckable(true); 1080 volume_button->setCheckable(true);
1079 UpdateVolumeUI(); 1081 UpdateVolumeUI();
1082 connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
1083 Settings::values.audio_muted = false;
1084 const auto volume = static_cast<u8>(percentage);
1085 Settings::values.volume.SetValue(volume);
1086 UpdateVolumeUI();
1087 });
1080 connect(volume_button, &QPushButton::clicked, this, [&] { 1088 connect(volume_button, &QPushButton::clicked, this, [&] {
1081 UpdateVolumeUI(); 1089 UpdateVolumeUI();
1082 volume_popup->setVisible(!volume_popup->isVisible()); 1090 volume_popup->setVisible(!volume_popup->isVisible());
@@ -1128,7 +1136,7 @@ void GMainWindow::InitializeWidgets() {
1128 connect(aa_status_button, &QPushButton::customContextMenuRequested, 1136 connect(aa_status_button, &QPushButton::customContextMenuRequested,
1129 [this](const QPoint& menu_location) { 1137 [this](const QPoint& menu_location) {
1130 QMenu context_menu; 1138 QMenu context_menu;
1131 for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { 1139 for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) {
1132 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { 1140 context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] {
1133 Settings::values.anti_aliasing.SetValue(aa_text_pair.first); 1141 Settings::values.anti_aliasing.SetValue(aa_text_pair.first);
1134 UpdateAAText(); 1142 UpdateAAText();
@@ -1152,7 +1160,7 @@ void GMainWindow::InitializeWidgets() {
1152 connect(filter_status_button, &QPushButton::customContextMenuRequested, 1160 connect(filter_status_button, &QPushButton::customContextMenuRequested,
1153 [this](const QPoint& menu_location) { 1161 [this](const QPoint& menu_location) {
1154 QMenu context_menu; 1162 QMenu context_menu;
1155 for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { 1163 for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) {
1156 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { 1164 context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] {
1157 Settings::values.scaling_filter.SetValue(filter_text_pair.first); 1165 Settings::values.scaling_filter.SetValue(filter_text_pair.first);
1158 UpdateFilterText(); 1166 UpdateFilterText();
@@ -1175,7 +1183,7 @@ void GMainWindow::InitializeWidgets() {
1175 [this](const QPoint& menu_location) { 1183 [this](const QPoint& menu_location) {
1176 QMenu context_menu; 1184 QMenu context_menu;
1177 1185
1178 for (auto const& pair : Config::use_docked_mode_texts_map) { 1186 for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) {
1179 context_menu.addAction(pair.second, [this, &pair] { 1187 context_menu.addAction(pair.second, [this, &pair] {
1180 if (pair.first != Settings::values.use_docked_mode.GetValue()) { 1188 if (pair.first != Settings::values.use_docked_mode.GetValue()) {
1181 OnToggleDockedMode(); 1189 OnToggleDockedMode();
@@ -1199,7 +1207,7 @@ void GMainWindow::InitializeWidgets() {
1199 [this](const QPoint& menu_location) { 1207 [this](const QPoint& menu_location) {
1200 QMenu context_menu; 1208 QMenu context_menu;
1201 1209
1202 for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { 1210 for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) {
1203 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { 1211 if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) {
1204 continue; 1212 continue;
1205 } 1213 }
@@ -1228,7 +1236,8 @@ void GMainWindow::InitializeWidgets() {
1228 [this](const QPoint& menu_location) { 1236 [this](const QPoint& menu_location) {
1229 QMenu context_menu; 1237 QMenu context_menu;
1230 1238
1231 for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { 1239 for (auto const& renderer_backend_pair :
1240 ConfigurationShared::renderer_backend_texts_map) {
1232 if (renderer_backend_pair.first == Settings::RendererBackend::Null) { 1241 if (renderer_backend_pair.first == Settings::RendererBackend::Null) {
1233 continue; 1242 continue;
1234 } 1243 }
@@ -1293,16 +1302,17 @@ void GMainWindow::InitializeRecentFileMenuActions() {
1293 1302
1294void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, 1303void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name,
1295 const bool tas_allowed) { 1304 const bool tas_allowed) {
1296 static const QString main_window = QStringLiteral("Main Window"); 1305 static const auto main_window = std::string("Main Window");
1297 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); 1306 action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString()));
1298 action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); 1307 action->setShortcutContext(
1308 hotkey_registry.GetShortcutContext(main_window, action_name.toStdString()));
1299 action->setAutoRepeat(false); 1309 action->setAutoRepeat(false);
1300 1310
1301 this->addAction(action); 1311 this->addAction(action);
1302 1312
1303 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1313 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1304 const auto* controller_hotkey = 1314 const auto* controller_hotkey =
1305 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1315 hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller);
1306 connect( 1316 connect(
1307 controller_hotkey, &ControllerShortcut::Activated, this, 1317 controller_hotkey, &ControllerShortcut::Activated, this,
1308 [action, tas_allowed, this] { 1318 [action, tas_allowed, this] {
@@ -1334,10 +1344,11 @@ void GMainWindow::InitializeHotkeys() {
1334 1344
1335 static const QString main_window = QStringLiteral("Main Window"); 1345 static const QString main_window = QStringLiteral("Main Window");
1336 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { 1346 const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) {
1337 const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); 1347 const auto* hotkey =
1348 hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this);
1338 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); 1349 auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
1339 const auto* controller_hotkey = 1350 const auto* controller_hotkey = hotkey_registry.GetControllerHotkey(
1340 hotkey_registry.GetControllerHotkey(main_window, action_name, controller); 1351 main_window.toStdString(), action_name.toStdString(), controller);
1341 connect(hotkey, &QShortcut::activated, this, function); 1352 connect(hotkey, &QShortcut::activated, this, function);
1342 connect(controller_hotkey, &ControllerShortcut::Activated, this, function, 1353 connect(controller_hotkey, &ControllerShortcut::Activated, this, function,
1343 Qt::QueuedConnection); 1354 Qt::QueuedConnection);
@@ -1574,6 +1585,7 @@ void GMainWindow::ConnectMenuEvents() {
1574 connect_menu(ui->action_Load_Cabinet_Formatter, 1585 connect_menu(ui->action_Load_Cabinet_Formatter,
1575 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); 1586 [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); });
1576 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); 1587 connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit);
1588 connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu);
1577 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); 1589 connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot);
1578 1590
1579 // TAS 1591 // TAS
@@ -1601,14 +1613,13 @@ void GMainWindow::UpdateMenuState() {
1601 ui->action_Pause, 1613 ui->action_Pause,
1602 }; 1614 };
1603 1615
1604 const std::array applet_actions{ 1616 const std::array applet_actions{ui->action_Load_Album,
1605 ui->action_Load_Album, 1617 ui->action_Load_Cabinet_Nickname_Owner,
1606 ui->action_Load_Cabinet_Nickname_Owner, 1618 ui->action_Load_Cabinet_Eraser,
1607 ui->action_Load_Cabinet_Eraser, 1619 ui->action_Load_Cabinet_Restorer,
1608 ui->action_Load_Cabinet_Restorer, 1620 ui->action_Load_Cabinet_Formatter,
1609 ui->action_Load_Cabinet_Formatter, 1621 ui->action_Load_Mii_Edit,
1610 ui->action_Load_Mii_Edit, 1622 ui->action_Open_Controller_Menu};
1611 };
1612 1623
1613 for (QAction* action : running_actions) { 1624 for (QAction* action : running_actions) {
1614 action->setEnabled(emulation_running); 1625 action->setEnabled(emulation_running);
@@ -1917,7 +1928,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1917 // Save configurations 1928 // Save configurations
1918 UpdateUISettings(); 1929 UpdateUISettings();
1919 game_list->SaveInterfaceLayout(); 1930 game_list->SaveInterfaceLayout();
1920 config->Save(); 1931 config->SaveAllValues();
1921 1932
1922 u64 title_id{0}; 1933 u64 title_id{0};
1923 1934
@@ -1935,7 +1946,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1935 const auto config_file_name = title_id == 0 1946 const auto config_file_name = title_id == 0
1936 ? Common::FS::PathToUTF8String(file_path.filename()) 1947 ? Common::FS::PathToUTF8String(file_path.filename())
1937 : fmt::format("{:016X}", title_id); 1948 : fmt::format("{:016X}", title_id);
1938 Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); 1949 QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig);
1939 system->HIDCore().ReloadInputDevices(); 1950 system->HIDCore().ReloadInputDevices();
1940 system->ApplySettings(); 1951 system->ApplySettings();
1941 } 1952 }
@@ -2160,6 +2171,10 @@ void GMainWindow::OnEmulationStopped() {
2160 emu_frametime_label->setVisible(false); 2171 emu_frametime_label->setVisible(false);
2161 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); 2172 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
2162 2173
2174 if (!firmware_label->text().isEmpty()) {
2175 firmware_label->setVisible(true);
2176 }
2177
2163 current_game_path.clear(); 2178 current_game_path.clear();
2164 2179
2165 // When closing the game, destroy the GLWindow to clear the context after the game is closed 2180 // When closing the game, destroy the GLWindow to clear the context after the game is closed
@@ -2847,170 +2862,259 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
2847 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); 2862 QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory));
2848} 2863}
2849 2864
2850void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, 2865bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path,
2851 GameListShortcutTarget target) { 2866 const std::string& comment,
2852 // Get path to yuzu executable 2867 const std::filesystem::path& icon_path,
2853 const QStringList args = QApplication::arguments(); 2868 const std::filesystem::path& command,
2854 std::filesystem::path yuzu_command = args[0].toStdString(); 2869 const std::string& arguments, const std::string& categories,
2855 2870 const std::string& keywords, const std::string& name) try {
2856 // If relative path, make it an absolute path 2871#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD
2857 if (yuzu_command.c_str()[0] == '.') { 2872 std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop");
2858 yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; 2873 std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc);
2874 if (!shortcut_stream.is_open()) {
2875 LOG_ERROR(Frontend, "Failed to create shortcut");
2876 return false;
2859 } 2877 }
2860 2878 // TODO: Migrate fmt::print to std::print in futures STD C++ 23.
2861#if defined(__linux__) 2879 fmt::print(shortcut_stream, "[Desktop Entry]\n");
2862 // Warn once if we are making a shortcut to a volatile AppImage 2880 fmt::print(shortcut_stream, "Type=Application\n");
2863 const std::string appimage_ending = 2881 fmt::print(shortcut_stream, "Version=1.0\n");
2864 std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); 2882 fmt::print(shortcut_stream, "Name={}\n", name);
2865 if (yuzu_command.string().ends_with(appimage_ending) && 2883 if (!comment.empty()) {
2866 !UISettings::values.shortcut_already_warned) { 2884 fmt::print(shortcut_stream, "Comment={}\n", comment);
2867 if (QMessageBox::warning(this, tr("Create Shortcut"), 2885 }
2868 tr("This will create a shortcut to the current AppImage. This may " 2886 if (std::filesystem::is_regular_file(icon_path)) {
2869 "not work well if you update. Continue?"), 2887 fmt::print(shortcut_stream, "Icon={}\n", icon_path.string());
2870 QMessageBox::StandardButton::Ok | 2888 }
2871 QMessageBox::StandardButton::Cancel) == 2889 fmt::print(shortcut_stream, "TryExec={}\n", command.string());
2872 QMessageBox::StandardButton::Cancel) { 2890 fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments);
2873 return; 2891 if (!categories.empty()) {
2892 fmt::print(shortcut_stream, "Categories={}\n", categories);
2893 }
2894 if (!keywords.empty()) {
2895 fmt::print(shortcut_stream, "Keywords={}\n", keywords);
2896 }
2897 return true;
2898#elif defined(_WIN32) // Windows
2899 HRESULT hr = CoInitialize(nullptr);
2900 if (FAILED(hr)) {
2901 LOG_ERROR(Frontend, "CoInitialize failed");
2902 return false;
2903 }
2904 SCOPE_EXIT({ CoUninitialize(); });
2905 IShellLinkW* ps1 = nullptr;
2906 IPersistFile* persist_file = nullptr;
2907 SCOPE_EXIT({
2908 if (persist_file != nullptr) {
2909 persist_file->Release();
2874 } 2910 }
2875 UISettings::values.shortcut_already_warned = true; 2911 if (ps1 != nullptr) {
2912 ps1->Release();
2913 }
2914 });
2915 HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
2916 reinterpret_cast<void**>(&ps1));
2917 if (FAILED(hres)) {
2918 LOG_ERROR(Frontend, "Failed to create IShellLinkW instance");
2919 return false;
2876 } 2920 }
2877#endif // __linux__ 2921 hres = ps1->SetPath(command.c_str());
2878 2922 if (FAILED(hres)) {
2879 std::filesystem::path target_directory{}; 2923 LOG_ERROR(Frontend, "Failed to set path");
2880 2924 return false;
2881 switch (target) {
2882 case GameListShortcutTarget::Desktop: {
2883 const QString desktop_path =
2884 QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
2885 target_directory = desktop_path.toUtf8().toStdString();
2886 break;
2887 } 2925 }
2888 case GameListShortcutTarget::Applications: { 2926 if (!arguments.empty()) {
2889 const QString applications_path = 2927 hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data());
2890 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); 2928 if (FAILED(hres)) {
2891 if (applications_path.isEmpty()) { 2929 LOG_ERROR(Frontend, "Failed to set arguments");
2892 const char* home = std::getenv("HOME"); 2930 return false;
2893 if (home != nullptr) {
2894 target_directory = std::filesystem::path(home) / ".local/share/applications";
2895 }
2896 } else {
2897 target_directory = applications_path.toUtf8().toStdString();
2898 } 2931 }
2899 break;
2900 } 2932 }
2901 default: 2933 if (!comment.empty()) {
2902 return; 2934 hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data());
2935 if (FAILED(hres)) {
2936 LOG_ERROR(Frontend, "Failed to set description");
2937 return false;
2938 }
2903 } 2939 }
2904 2940 if (std::filesystem::is_regular_file(icon_path)) {
2905 const QDir dir(QString::fromStdString(target_directory.generic_string())); 2941 hres = ps1->SetIconLocation(icon_path.c_str(), 0);
2906 if (!dir.exists()) { 2942 if (FAILED(hres)) {
2907 QMessageBox::critical(this, tr("Create Shortcut"), 2943 LOG_ERROR(Frontend, "Failed to set icon location");
2908 tr("Cannot create shortcut. Path \"%1\" does not exist.") 2944 return false;
2909 .arg(QString::fromStdString(target_directory.generic_string())), 2945 }
2910 QMessageBox::StandardButton::Ok);
2911 return;
2912 } 2946 }
2947 hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&persist_file));
2948 if (FAILED(hres)) {
2949 LOG_ERROR(Frontend, "Failed to get IPersistFile interface");
2950 return false;
2951 }
2952 hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE);
2953 if (FAILED(hres)) {
2954 LOG_ERROR(Frontend, "Failed to save shortcut");
2955 return false;
2956 }
2957 return true;
2958#else // Unsupported platform
2959 return false;
2960#endif
2961} catch (const std::exception& e) {
2962 LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what());
2963 return false;
2964}
2965// Messages in pre-defined message boxes for less code spaghetti
2966bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) {
2967 int result = 0;
2968 QMessageBox::StandardButtons buttons;
2969 switch (imsg) {
2970 case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES:
2971 buttons = QMessageBox::Yes | QMessageBox::No;
2972 result =
2973 QMessageBox::information(parent, tr("Create Shortcut"),
2974 tr("Do you want to launch the game in fullscreen?"), buttons);
2975 return result == QMessageBox::Yes;
2976 case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS:
2977 QMessageBox::information(parent, tr("Create Shortcut"),
2978 tr("Successfully created a shortcut to %1").arg(game_title));
2979 return false;
2980 case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING:
2981 buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel;
2982 result =
2983 QMessageBox::warning(this, tr("Create Shortcut"),
2984 tr("This will create a shortcut to the current AppImage. This may "
2985 "not work well if you update. Continue?"),
2986 buttons);
2987 return result == QMessageBox::Ok;
2988 default:
2989 buttons = QMessageBox::Ok;
2990 QMessageBox::critical(parent, tr("Create Shortcut"),
2991 tr("Failed to create a shortcut to %1").arg(game_title), buttons);
2992 return false;
2993 }
2994}
2913 2995
2914 const std::string game_file_name = std::filesystem::path(game_path).filename().string(); 2996bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
2915 // Determine full paths for icon and shortcut 2997 std::filesystem::path& out_icon_path) {
2916#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) 2998 // Get path to Yuzu icons directory & icon extension
2917 const char* home = std::getenv("HOME"); 2999 std::string ico_extension = "png";
2918 const std::filesystem::path home_path = (home == nullptr ? "~" : home); 3000#if defined(_WIN32)
2919 const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); 3001 out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir);
2920 3002 ico_extension = "ico";
2921 std::filesystem::path system_icons_path = 3003#elif defined(__linux__) || defined(__FreeBSD__)
2922 (xdg_data_home == nullptr ? home_path / ".local/share/" 3004 out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256";
2923 : std::filesystem::path(xdg_data_home)) / 3005#endif
2924 "icons/hicolor/256x256"; 3006 // Create icons directory if it doesn't exist
2925 if (!Common::FS::CreateDirs(system_icons_path)) { 3007 if (!Common::FS::CreateDirs(out_icon_path)) {
2926 QMessageBox::critical( 3008 QMessageBox::critical(
2927 this, tr("Create Icon"), 3009 this, tr("Create Icon"),
2928 tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") 3010 tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.")
2929 .arg(QString::fromStdString(system_icons_path)), 3011 .arg(QString::fromStdString(out_icon_path.string())),
2930 QMessageBox::StandardButton::Ok); 3012 QMessageBox::StandardButton::Ok);
2931 return; 3013 out_icon_path.clear();
3014 return false;
2932 } 3015 }
2933 std::filesystem::path icon_path =
2934 system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name)
2935 : fmt::format("yuzu-{:016X}.png", program_id));
2936 const std::filesystem::path shortcut_path =
2937 target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name)
2938 : fmt::format("yuzu-{:016X}.desktop", program_id));
2939#elif defined(WIN32)
2940 std::filesystem::path icons_path =
2941 Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir);
2942 std::filesystem::path icon_path =
2943 icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name)
2944 : fmt::format("yuzu-{:016X}.ico", program_id)));
2945#else
2946 std::string icon_extension;
2947#endif
2948
2949 // Get title from game file
2950 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
2951 system->GetContentProvider()};
2952 const auto control = pm.GetControlMetadata();
2953 const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
2954
2955 std::string title{fmt::format("{:016X}", program_id)};
2956 3016
2957 if (control.first != nullptr) { 3017 // Create icon file path
2958 title = control.first->GetApplicationName(); 3018 out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension)
2959 } else { 3019 : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension));
2960 loader->ReadTitle(title); 3020 return true;
2961 } 3021}
2962 3022
2963 // Get icon from game file 3023void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
2964 std::vector<u8> icon_image_file{}; 3024 GameListShortcutTarget target) {
2965 if (control.second != nullptr) { 3025 std::string game_title;
2966 icon_image_file = control.second->ReadAllBytes(); 3026 QString qt_game_title;
2967 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { 3027 std::filesystem::path out_icon_path;
2968 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); 3028 // Get path to yuzu executable
3029 const QStringList args = QApplication::arguments();
3030 std::filesystem::path yuzu_command = args[0].toStdString();
3031 // If relative path, make it an absolute path
3032 if (yuzu_command.c_str()[0] == '.') {
3033 yuzu_command = Common::FS::GetCurrentDir() / yuzu_command;
2969 } 3034 }
2970 3035 // Shortcut path
2971 QImage icon_data = 3036 std::filesystem::path shortcut_path{};
2972 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); 3037 if (target == GameListShortcutTarget::Desktop) {
2973#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) 3038 shortcut_path =
2974 // Convert and write the icon as a PNG 3039 QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString();
2975 if (!icon_data.save(QString::fromStdString(icon_path.string()))) { 3040 } else if (target == GameListShortcutTarget::Applications) {
2976 LOG_ERROR(Frontend, "Could not write icon as PNG to file"); 3041 shortcut_path =
3042 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
3043 }
3044 // Icon path and title
3045 if (std::filesystem::exists(shortcut_path)) {
3046 // Get title from game file
3047 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
3048 system->GetContentProvider()};
3049 const auto control = pm.GetControlMetadata();
3050 const auto loader =
3051 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read));
3052 game_title = fmt::format("{:016X}", program_id);
3053 if (control.first != nullptr) {
3054 game_title = control.first->GetApplicationName();
3055 } else {
3056 loader->ReadTitle(game_title);
3057 }
3058 // Delete illegal characters from title
3059 const std::string illegal_chars = "<>:\"/\\|?*.";
3060 for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
3061 if (illegal_chars.find(*it) != std::string::npos) {
3062 game_title.erase(it.base() - 1);
3063 }
3064 }
3065 qt_game_title = QString::fromStdString(game_title);
3066 // Get icon from game file
3067 std::vector<u8> icon_image_file{};
3068 if (control.second != nullptr) {
3069 icon_image_file = control.second->ReadAllBytes();
3070 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
3071 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
3072 }
3073 QImage icon_data =
3074 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
3075 if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
3076 if (!SaveIconToFile(out_icon_path, icon_data)) {
3077 LOG_ERROR(Frontend, "Could not write icon to file");
3078 }
3079 }
2977 } else { 3080 } else {
2978 LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); 3081 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
2979 } 3082 qt_game_title);
2980#elif defined(WIN32) 3083 LOG_ERROR(Frontend, "Invalid shortcut target");
2981 if (!SaveIconToFile(icon_path.string(), icon_data)) {
2982 LOG_ERROR(Frontend, "Could not write icon to file");
2983 return; 3084 return;
2984 } 3085 }
3086#if defined(__linux__)
3087 // Special case for AppImages
3088 // Warn once if we are making a shortcut to a volatile AppImage
3089 const std::string appimage_ending =
3090 std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage");
3091 if (yuzu_command.string().ends_with(appimage_ending) &&
3092 !UISettings::values.shortcut_already_warned) {
3093 if (GMainWindow::CreateShortcutMessagesGUI(
3094 this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) {
3095 return;
3096 }
3097 UISettings::values.shortcut_already_warned = true;
3098 }
2985#endif // __linux__ 3099#endif // __linux__
2986 3100 // Create shortcut
2987#ifdef _WIN32 3101 std::string arguments = fmt::format("-g \"{:s}\"", game_path);
2988 // Replace characters that are illegal in Windows filenames by a dash 3102 if (GMainWindow::CreateShortcutMessagesGUI(
2989 const std::string illegal_chars = "<>:\"/\\|?*"; 3103 this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) {
2990 for (char c : illegal_chars) { 3104 arguments = "-f " + arguments;
2991 std::replace(title.begin(), title.end(), c, '_');
2992 } 3105 }
2993 const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); 3106 const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title);
2994#endif
2995
2996 const std::string comment =
2997 tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString();
2998 const std::string arguments = fmt::format("-g \"{:s}\"", game_path);
2999 const std::string categories = "Game;Emulator;Qt;"; 3107 const std::string categories = "Game;Emulator;Qt;";
3000 const std::string keywords = "Switch;Nintendo;"; 3108 const std::string keywords = "Switch;Nintendo;";
3001 3109
3002 if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), 3110 if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command,
3003 yuzu_command.string(), arguments, categories, keywords)) { 3111 arguments, categories, keywords, game_title)) {
3004 QMessageBox::critical(this, tr("Create Shortcut"), 3112 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS,
3005 tr("Failed to create a shortcut at %1") 3113 qt_game_title);
3006 .arg(QString::fromStdString(shortcut_path.string())));
3007 return; 3114 return;
3008 } 3115 }
3009 3116 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
3010 LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); 3117 qt_game_title);
3011 QMessageBox::information(
3012 this, tr("Create Shortcut"),
3013 tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title)));
3014} 3118}
3015 3119
3016void GMainWindow::OnGameListOpenDirectory(const QString& directory) { 3120void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
@@ -3045,7 +3149,7 @@ void GMainWindow::OnGameListAddDirectory() {
3045 return; 3149 return;
3046 } 3150 }
3047 3151
3048 UISettings::GameDir game_dir{dir_path, false, true}; 3152 UISettings::GameDir game_dir{dir_path.toStdString(), false, true};
3049 if (!UISettings::values.game_dirs.contains(game_dir)) { 3153 if (!UISettings::values.game_dirs.contains(game_dir)) {
3050 UISettings::values.game_dirs.append(game_dir); 3154 UISettings::values.game_dirs.append(game_dir);
3051 game_list->PopulateAsync(UISettings::values.game_dirs); 3155 game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -3091,14 +3195,14 @@ void GMainWindow::OnMenuLoadFile() {
3091 "%1 is an identifier for the Switch executable file extensions.") 3195 "%1 is an identifier for the Switch executable file extensions.")
3092 .arg(extensions); 3196 .arg(extensions);
3093 const QString filename = QFileDialog::getOpenFileName( 3197 const QString filename = QFileDialog::getOpenFileName(
3094 this, tr("Load File"), UISettings::values.roms_path, file_filter); 3198 this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter);
3095 is_load_file_select_active = false; 3199 is_load_file_select_active = false;
3096 3200
3097 if (filename.isEmpty()) { 3201 if (filename.isEmpty()) {
3098 return; 3202 return;
3099 } 3203 }
3100 3204
3101 UISettings::values.roms_path = QFileInfo(filename).path(); 3205 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
3102 BootGame(filename); 3206 BootGame(filename);
3103} 3207}
3104 3208
@@ -3131,7 +3235,8 @@ void GMainWindow::OnMenuInstallToNAND() {
3131 "Image (*.xci)"); 3235 "Image (*.xci)");
3132 3236
3133 QStringList filenames = QFileDialog::getOpenFileNames( 3237 QStringList filenames = QFileDialog::getOpenFileNames(
3134 this, tr("Install Files"), UISettings::values.roms_path, file_filter); 3238 this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path),
3239 file_filter);
3135 3240
3136 if (filenames.isEmpty()) { 3241 if (filenames.isEmpty()) {
3137 return; 3242 return;
@@ -3149,7 +3254,7 @@ void GMainWindow::OnMenuInstallToNAND() {
3149 } 3254 }
3150 3255
3151 // Save folder location of the first selected file 3256 // Save folder location of the first selected file
3152 UISettings::values.roms_path = QFileInfo(filenames[0]).path(); 3257 UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString();
3153 3258
3154 int remaining = filenames.size(); 3259 int remaining = filenames.size();
3155 3260
@@ -3494,7 +3599,7 @@ void GMainWindow::OnExit() {
3494 3599
3495void GMainWindow::OnSaveConfig() { 3600void GMainWindow::OnSaveConfig() {
3496 system->ApplySettings(); 3601 system->ApplySettings();
3497 config->Save(); 3602 config->SaveAllValues();
3498} 3603}
3499 3604
3500void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 3605void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
@@ -3750,7 +3855,7 @@ void GMainWindow::OnConfigure() {
3750 3855
3751 Settings::values.disabled_addons.clear(); 3856 Settings::values.disabled_addons.clear();
3752 3857
3753 config = std::make_unique<Config>(); 3858 config = std::make_unique<QtConfig>();
3754 UISettings::values.reset_to_defaults = false; 3859 UISettings::values.reset_to_defaults = false;
3755 3860
3756 UISettings::values.game_dirs = std::move(old_game_dirs); 3861 UISettings::values.game_dirs = std::move(old_game_dirs);
@@ -3785,7 +3890,7 @@ void GMainWindow::OnConfigure() {
3785 3890
3786 UISettings::values.configuration_applied = false; 3891 UISettings::values.configuration_applied = false;
3787 3892
3788 config->Save(); 3893 config->SaveAllValues();
3789 3894
3790 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { 3895 if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) {
3791 render_window->installEventFilter(render_window); 3896 render_window->installEventFilter(render_window);
@@ -4001,68 +4106,8 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file
4001 UISettings::values.configuration_applied = false; 4106 UISettings::values.configuration_applied = false;
4002 4107
4003 if (!is_powered_on) { 4108 if (!is_powered_on) {
4004 config->Save(); 4109 config->SaveAllValues();
4005 }
4006}
4007
4008bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title,
4009 const std::string& comment, const std::string& icon_path,
4010 const std::string& command, const std::string& arguments,
4011 const std::string& categories, const std::string& keywords) {
4012#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
4013 // This desktop file template was writing referencing
4014 // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html
4015 std::string shortcut_contents{};
4016 shortcut_contents.append("[Desktop Entry]\n");
4017 shortcut_contents.append("Type=Application\n");
4018 shortcut_contents.append("Version=1.0\n");
4019 shortcut_contents.append(fmt::format("Name={:s}\n", title));
4020 shortcut_contents.append(fmt::format("Comment={:s}\n", comment));
4021 shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path));
4022 shortcut_contents.append(fmt::format("TryExec={:s}\n", command));
4023 shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments));
4024 shortcut_contents.append(fmt::format("Categories={:s}\n", categories));
4025 shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords));
4026
4027 std::ofstream shortcut_stream(shortcut_path);
4028 if (!shortcut_stream.is_open()) {
4029 LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path);
4030 return false;
4031 }
4032 shortcut_stream << shortcut_contents;
4033 shortcut_stream.close();
4034
4035 return true;
4036#elif defined(WIN32)
4037 IShellLinkW* shell_link;
4038 auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW,
4039 (void**)&shell_link);
4040 if (FAILED(hres)) {
4041 return false;
4042 }
4043 shell_link->SetPath(
4044 Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to
4045 shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data());
4046 shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data());
4047 shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0);
4048
4049 IPersistFile* persist_file;
4050 hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file);
4051 if (FAILED(hres)) {
4052 return false;
4053 } 4110 }
4054
4055 hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE);
4056 if (FAILED(hres)) {
4057 return false;
4058 }
4059
4060 persist_file->Release();
4061 shell_link->Release();
4062
4063 return true;
4064#endif
4065 return false;
4066} 4111}
4067 4112
4068void GMainWindow::OnLoadAmiibo() { 4113void GMainWindow::OnLoadAmiibo() {
@@ -4103,7 +4148,6 @@ void GMainWindow::OnLoadAmiibo() {
4103bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, 4148bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
4104 QMessageBox::StandardButtons buttons, 4149 QMessageBox::StandardButtons buttons,
4105 QMessageBox::StandardButton defaultButton) { 4150 QMessageBox::StandardButton defaultButton) {
4106
4107 QMessageBox* box_dialog = new QMessageBox(parent); 4151 QMessageBox* box_dialog = new QMessageBox(parent);
4108 box_dialog->setWindowTitle(title); 4152 box_dialog->setWindowTitle(title);
4109 box_dialog->setText(text); 4153 box_dialog->setText(text);
@@ -4295,7 +4339,7 @@ void GMainWindow::OnAlbum() {
4295 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); 4339 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer);
4296 4340
4297 const auto filename = QString::fromStdString(album_nca->GetFullPath()); 4341 const auto filename = QString::fromStdString(album_nca->GetFullPath());
4298 UISettings::values.roms_path = QFileInfo(filename).path(); 4342 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4299 BootGame(filename, AlbumId); 4343 BootGame(filename, AlbumId);
4300} 4344}
4301 4345
@@ -4319,7 +4363,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) {
4319 system->GetAppletManager().SetCabinetMode(mode); 4363 system->GetAppletManager().SetCabinetMode(mode);
4320 4364
4321 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); 4365 const auto filename = QString::fromStdString(cabinet_nca->GetFullPath());
4322 UISettings::values.roms_path = QFileInfo(filename).path(); 4366 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4323 BootGame(filename, CabinetId); 4367 BootGame(filename, CabinetId);
4324} 4368}
4325 4369
@@ -4342,10 +4386,35 @@ void GMainWindow::OnMiiEdit() {
4342 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); 4386 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit);
4343 4387
4344 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); 4388 const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath()));
4345 UISettings::values.roms_path = QFileInfo(filename).path(); 4389 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4346 BootGame(filename, MiiEditId); 4390 BootGame(filename, MiiEditId);
4347} 4391}
4348 4392
4393void GMainWindow::OnOpenControllerMenu() {
4394 constexpr u64 ControllerAppletId =
4395 static_cast<u64>(Service::AM::Applets::AppletProgramId::Controller);
4396 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4397 if (!bis_system) {
4398 QMessageBox::warning(this, tr("No firmware available"),
4399 tr("Please install the firmware to use the Controller Menu."));
4400 return;
4401 }
4402
4403 auto controller_applet_nca =
4404 bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program);
4405 if (!controller_applet_nca) {
4406 QMessageBox::warning(this, tr("Controller Applet"),
4407 tr("Controller Menu is not available. Please reinstall firmware."));
4408 return;
4409 }
4410
4411 system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller);
4412
4413 const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath()));
4414 UISettings::values.roms_path = QFileInfo(filename).path().toStdString();
4415 BootGame(filename, ControllerAppletId);
4416}
4417
4349void GMainWindow::OnCaptureScreenshot() { 4418void GMainWindow::OnCaptureScreenshot() {
4350 if (emu_thread == nullptr || !emu_thread->IsRunning()) { 4419 if (emu_thread == nullptr || !emu_thread->IsRunning()) {
4351 return; 4420 return;
@@ -4532,11 +4601,13 @@ void GMainWindow::UpdateStatusBar() {
4532 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); 4601 emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue());
4533 game_fps_label->setVisible(true); 4602 game_fps_label->setVisible(true);
4534 emu_frametime_label->setVisible(true); 4603 emu_frametime_label->setVisible(true);
4604 firmware_label->setVisible(false);
4535} 4605}
4536 4606
4537void GMainWindow::UpdateGPUAccuracyButton() { 4607void GMainWindow::UpdateGPUAccuracyButton() {
4538 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); 4608 const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue();
4539 const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; 4609 const auto gpu_accuracy_text =
4610 ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second;
4540 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); 4611 gpu_accuracy_button->setText(gpu_accuracy_text.toUpper());
4541 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); 4612 gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal);
4542} 4613}
@@ -4545,31 +4616,32 @@ void GMainWindow::UpdateDockedButton() {
4545 const auto console_mode = Settings::values.use_docked_mode.GetValue(); 4616 const auto console_mode = Settings::values.use_docked_mode.GetValue();
4546 dock_status_button->setChecked(Settings::IsDockedMode()); 4617 dock_status_button->setChecked(Settings::IsDockedMode());
4547 dock_status_button->setText( 4618 dock_status_button->setText(
4548 Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); 4619 ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper());
4549} 4620}
4550 4621
4551void GMainWindow::UpdateAPIText() { 4622void GMainWindow::UpdateAPIText() {
4552 const auto api = Settings::values.renderer_backend.GetValue(); 4623 const auto api = Settings::values.renderer_backend.GetValue();
4553 const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; 4624 const auto renderer_status_text =
4625 ConfigurationShared::renderer_backend_texts_map.find(api)->second;
4554 renderer_status_button->setText( 4626 renderer_status_button->setText(
4555 api == Settings::RendererBackend::OpenGL 4627 api == Settings::RendererBackend::OpenGL
4556 ? tr("%1 %2").arg( 4628 ? tr("%1 %2").arg(renderer_status_text.toUpper(),
4557 renderer_status_text.toUpper(), 4629 ConfigurationShared::shader_backend_texts_map
4558 Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) 4630 .find(Settings::values.shader_backend.GetValue())
4559 ->second) 4631 ->second)
4560 : renderer_status_text.toUpper()); 4632 : renderer_status_text.toUpper());
4561} 4633}
4562 4634
4563void GMainWindow::UpdateFilterText() { 4635void GMainWindow::UpdateFilterText() {
4564 const auto filter = Settings::values.scaling_filter.GetValue(); 4636 const auto filter = Settings::values.scaling_filter.GetValue();
4565 const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; 4637 const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second;
4566 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") 4638 filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR")
4567 : filter_text.toUpper()); 4639 : filter_text.toUpper());
4568} 4640}
4569 4641
4570void GMainWindow::UpdateAAText() { 4642void GMainWindow::UpdateAAText() {
4571 const auto aa_mode = Settings::values.anti_aliasing.GetValue(); 4643 const auto aa_mode = Settings::values.anti_aliasing.GetValue();
4572 const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; 4644 const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second;
4573 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None 4645 aa_status_button->setText(aa_mode == Settings::AntiAliasing::None
4574 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) 4646 ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA"))
4575 : aa_text.toUpper()); 4647 : aa_text.toUpper());
@@ -4749,6 +4821,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
4749 "games.")); 4821 "games."));
4750 } 4822 }
4751 4823
4824 SetFirmwareVersion();
4825
4752 if (behavior == ReinitializeKeyBehavior::Warning) { 4826 if (behavior == ReinitializeKeyBehavior::Warning) {
4753 game_list->PopulateAsync(UISettings::values.game_dirs); 4827 game_list->PopulateAsync(UISettings::values.game_dirs);
4754 } 4828 }
@@ -4776,7 +4850,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() {
4776} 4850}
4777 4851
4778bool GMainWindow::CheckFirmwarePresence() { 4852bool GMainWindow::CheckFirmwarePresence() {
4779 constexpr u64 MiiEditId = 0x0100000000001009ull; 4853 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
4780 4854
4781 auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); 4855 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4782 if (!bis_system) { 4856 if (!bis_system) {
@@ -4791,6 +4865,28 @@ bool GMainWindow::CheckFirmwarePresence() {
4791 return true; 4865 return true;
4792} 4866}
4793 4867
4868void GMainWindow::SetFirmwareVersion() {
4869 Service::Set::FirmwareVersionFormat firmware_data{};
4870 const auto result = Service::Set::GetFirmwareVersionImpl(
4871 firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2);
4872
4873 if (result.IsError() || !CheckFirmwarePresence()) {
4874 LOG_INFO(Frontend, "Installed firmware: No firmware available");
4875 firmware_label->setVisible(false);
4876 return;
4877 }
4878
4879 firmware_label->setVisible(true);
4880
4881 const std::string display_version(firmware_data.display_version.data());
4882 const std::string display_title(firmware_data.display_title.data());
4883
4884 LOG_INFO(Frontend, "Installed firmware: {}", display_title);
4885
4886 firmware_label->setText(QString::fromStdString(display_version));
4887 firmware_label->setToolTip(QString::fromStdString(display_title));
4888}
4889
4794bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, 4890bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id,
4795 u64* selected_title_id, u8* selected_content_record_type) { 4891 u64* selected_title_id, u8* selected_content_record_type) {
4796 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>; 4892 using ContentInfo = std::tuple<u64, FileSys::TitleType, FileSys::ContentRecordType>;
@@ -4872,6 +4968,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
4872 4968
4873 UpdateUISettings(); 4969 UpdateUISettings();
4874 game_list->SaveInterfaceLayout(); 4970 game_list->SaveInterfaceLayout();
4971 UISettings::SaveWindowState();
4875 hotkey_registry.SaveHotkeys(); 4972 hotkey_registry.SaveHotkeys();
4876 4973
4877 // Unload controllers early 4974 // Unload controllers early
@@ -5026,9 +5123,9 @@ static void AdjustLinkColor() {
5026} 5123}
5027 5124
5028void GMainWindow::UpdateUITheme() { 5125void GMainWindow::UpdateUITheme() {
5029 const QString default_theme = 5126 const QString default_theme = QString::fromUtf8(
5030 QString::fromUtf8(UISettings::themes[static_cast<size_t>(Config::default_theme)].second); 5127 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second);
5031 QString current_theme = UISettings::values.theme; 5128 QString current_theme = QString::fromStdString(UISettings::values.theme);
5032 5129
5033 if (current_theme.isEmpty()) { 5130 if (current_theme.isEmpty()) {
5034 current_theme = default_theme; 5131 current_theme = default_theme;
@@ -5056,7 +5153,7 @@ void GMainWindow::UpdateUITheme() {
5056 QFile f(theme_uri); 5153 QFile f(theme_uri);
5057 if (!f.open(QFile::ReadOnly | QFile::Text)) { 5154 if (!f.open(QFile::ReadOnly | QFile::Text)) {
5058 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", 5155 LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme",
5059 UISettings::values.theme.toStdString()); 5156 UISettings::values.theme);
5060 current_theme = default_theme; 5157 current_theme = default_theme;
5061 } 5158 }
5062 } 5159 }
@@ -5069,7 +5166,7 @@ void GMainWindow::UpdateUITheme() {
5069 setStyleSheet(ts.readAll()); 5166 setStyleSheet(ts.readAll());
5070 } else { 5167 } else {
5071 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", 5168 LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found",
5072 UISettings::values.theme.toStdString()); 5169 UISettings::values.theme);
5073 qApp->setStyleSheet({}); 5170 qApp->setStyleSheet({});
5074 setStyleSheet({}); 5171 setStyleSheet({});
5075 } 5172 }
@@ -5078,27 +5175,28 @@ void GMainWindow::UpdateUITheme() {
5078void GMainWindow::LoadTranslation() { 5175void GMainWindow::LoadTranslation() {
5079 bool loaded; 5176 bool loaded;
5080 5177
5081 if (UISettings::values.language.isEmpty()) { 5178 if (UISettings::values.language.empty()) {
5082 // If the selected language is empty, use system locale 5179 // If the selected language is empty, use system locale
5083 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); 5180 loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
5084 } else { 5181 } else {
5085 // Otherwise load from the specified file 5182 // Otherwise load from the specified file
5086 loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); 5183 loaded = translator.load(QString::fromStdString(UISettings::values.language),
5184 QStringLiteral(":/languages/"));
5087 } 5185 }
5088 5186
5089 if (loaded) { 5187 if (loaded) {
5090 qApp->installTranslator(&translator); 5188 qApp->installTranslator(&translator);
5091 } else { 5189 } else {
5092 UISettings::values.language = QStringLiteral("en"); 5190 UISettings::values.language = std::string("en");
5093 } 5191 }
5094} 5192}
5095 5193
5096void GMainWindow::OnLanguageChanged(const QString& locale) { 5194void GMainWindow::OnLanguageChanged(const QString& locale) {
5097 if (UISettings::values.language != QStringLiteral("en")) { 5195 if (UISettings::values.language != std::string("en")) {
5098 qApp->removeTranslator(&translator); 5196 qApp->removeTranslator(&translator);
5099 } 5197 }
5100 5198
5101 UISettings::values.language = locale; 5199 UISettings::values.language = locale.toStdString();
5102 LoadTranslation(); 5200 LoadTranslation();
5103 ui->retranslateUi(this); 5201 ui->retranslateUi(this);
5104 multiplayer_state->retranslateUi(); 5202 multiplayer_state->retranslateUi();
@@ -5124,7 +5222,7 @@ void GMainWindow::changeEvent(QEvent* event) {
5124 // UpdateUITheme is a decent work around 5222 // UpdateUITheme is a decent work around
5125 if (event->type() == QEvent::PaletteChange) { 5223 if (event->type() == QEvent::PaletteChange) {
5126 const QPalette test_palette(qApp->palette()); 5224 const QPalette test_palette(qApp->palette());
5127 const QString current_theme = UISettings::values.theme; 5225 const QString current_theme = QString::fromStdString(UISettings::values.theme);
5128 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too 5226 // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too
5129 static QColor last_window_color; 5227 static QColor last_window_color;
5130 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); 5228 const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window);
@@ -5218,7 +5316,8 @@ static void SetHighDPIAttributes() {
5218} 5316}
5219 5317
5220int main(int argc, char* argv[]) { 5318int main(int argc, char* argv[]) {
5221 std::unique_ptr<Config> config = std::make_unique<Config>(); 5319 std::unique_ptr<QtConfig> config = std::make_unique<QtConfig>();
5320 UISettings::RestoreWindowState(config);
5222 bool has_broken_vulkan = false; 5321 bool has_broken_vulkan = false;
5223 bool is_child = false; 5322 bool is_child = false;
5224 if (CheckEnvVars(&is_child)) { 5323 if (CheckEnvVars(&is_child)) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index f9c6efe4f..e99d58995 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include <optional> 7#include <optional>
8 8
9#include <filesystem>
9#include <QMainWindow> 10#include <QMainWindow>
10#include <QMessageBox> 11#include <QMessageBox>
11#include <QPushButton> 12#include <QPushButton>
@@ -14,6 +15,7 @@
14 15
15#include "common/announce_multiplayer_room.h" 16#include "common/announce_multiplayer_room.h"
16#include "common/common_types.h" 17#include "common/common_types.h"
18#include "configuration/qt_config.h"
17#include "input_common/drivers/tas_input.h" 19#include "input_common/drivers/tas_input.h"
18#include "yuzu/compatibility_list.h" 20#include "yuzu/compatibility_list.h"
19#include "yuzu/hotkeys.h" 21#include "yuzu/hotkeys.h"
@@ -25,7 +27,7 @@
25#include <QtDBus/QtDBus> 27#include <QtDBus/QtDBus>
26#endif 28#endif
27 29
28class Config; 30class QtConfig;
29class ClickableLabel; 31class ClickableLabel;
30class EmuThread; 32class EmuThread;
31class GameList; 33class GameList;
@@ -174,10 +176,17 @@ class GMainWindow : public QMainWindow {
174 UI_EMU_STOPPING, 176 UI_EMU_STOPPING,
175 }; 177 };
176 178
179 enum {
180 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
181 CREATE_SHORTCUT_MSGBOX_SUCCESS,
182 CREATE_SHORTCUT_MSGBOX_ERROR,
183 CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING,
184 };
185
177public: 186public:
178 void filterBarSetChecked(bool state); 187 void filterBarSetChecked(bool state);
179 void UpdateUITheme(); 188 void UpdateUITheme();
180 explicit GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan); 189 explicit GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulkan);
181 ~GMainWindow() override; 190 ~GMainWindow() override;
182 191
183 bool DropAction(QDropEvent* event); 192 bool DropAction(QDropEvent* event);
@@ -402,6 +411,7 @@ private slots:
402 void OnAlbum(); 411 void OnAlbum();
403 void OnCabinet(Service::NFP::CabinetMode mode); 412 void OnCabinet(Service::NFP::CabinetMode mode);
404 void OnMiiEdit(); 413 void OnMiiEdit();
414 void OnOpenControllerMenu();
405 void OnCaptureScreenshot(); 415 void OnCaptureScreenshot();
406 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 416 void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
407 void OnLanguageChanged(const QString& locale); 417 void OnLanguageChanged(const QString& locale);
@@ -448,6 +458,7 @@ private:
448 bool CheckDarkMode(); 458 bool CheckDarkMode();
449 bool CheckSystemArchiveDecryption(); 459 bool CheckSystemArchiveDecryption();
450 bool CheckFirmwarePresence(); 460 bool CheckFirmwarePresence();
461 void SetFirmwareVersion();
451 void ConfigureFilesystemProvider(const std::string& filepath); 462 void ConfigureFilesystemProvider(const std::string& filepath);
452 /** 463 /**
453 * 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
@@ -456,11 +467,14 @@ private:
456 bool ConfirmShutdownGame(); 467 bool ConfirmShutdownGame();
457 468
458 QString GetTasStateDescription() const; 469 QString GetTasStateDescription() const;
459 bool CreateShortcut(const std::string& shortcut_path, const std::string& title, 470 bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title);
460 const std::string& comment, const std::string& icon_path, 471 bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name,
461 const std::string& command, const std::string& arguments, 472 std::filesystem::path& out_icon_path);
462 const std::string& categories, const std::string& keywords); 473 bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment,
463 474 const std::filesystem::path& icon_path,
475 const std::filesystem::path& command, const std::string& arguments,
476 const std::string& categories, const std::string& keywords,
477 const std::string& name);
464 /** 478 /**
465 * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog 479 * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
466 * The only difference is that it returns a boolean. 480 * The only difference is that it returns a boolean.
@@ -499,6 +513,7 @@ private:
499 QLabel* game_fps_label = nullptr; 513 QLabel* game_fps_label = nullptr;
500 QLabel* emu_frametime_label = nullptr; 514 QLabel* emu_frametime_label = nullptr;
501 QLabel* tas_label = nullptr; 515 QLabel* tas_label = nullptr;
516 QLabel* firmware_label = nullptr;
502 QPushButton* gpu_accuracy_button = nullptr; 517 QPushButton* gpu_accuracy_button = nullptr;
503 QPushButton* renderer_status_button = nullptr; 518 QPushButton* renderer_status_button = nullptr;
504 QPushButton* dock_status_button = nullptr; 519 QPushButton* dock_status_button = nullptr;
@@ -509,7 +524,7 @@ private:
509 QSlider* volume_slider = nullptr; 524 QSlider* volume_slider = nullptr;
510 QTimer status_bar_update_timer; 525 QTimer status_bar_update_timer;
511 526
512 std::unique_ptr<Config> config; 527 std::unique_ptr<QtConfig> config;
513 528
514 // Whether emulation is currently running in yuzu. 529 // Whether emulation is currently running in yuzu.
515 bool emulation_running = false; 530 bool emulation_running = false;
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 77d992c54..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 {
@@ -109,9 +118,13 @@ struct Values {
109 Settings::Specialization::Default, 118 Settings::Specialization::Default,
110 true, 119 true,
111 true}; 120 true};
112 Setting<bool> mute_when_in_background{ 121 Setting<bool> mute_when_in_background{linkage,
113 linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, 122 false,
114 true, true}; 123 "muteWhenInBackground",
124 Category::UiAudio,
125 Settings::Specialization::Default,
126 true,
127 true};
115 Setting<bool> hide_mouse{ 128 Setting<bool> hide_mouse{
116 linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, 129 linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default,
117 true, true}; 130 true, true};
@@ -140,15 +153,15 @@ struct Values {
140 Category::Screenshots}; 153 Category::Screenshots};
141 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; 154 Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
142 155
143 QString roms_path; 156 std::string roms_path;
144 QString symbols_path; 157 std::string symbols_path;
145 QString game_dir_deprecated; 158 std::string game_dir_deprecated;
146 bool game_dir_deprecated_deepscan; 159 bool game_dir_deprecated_deepscan;
147 QVector<UISettings::GameDir> game_dirs; 160 QVector<GameDir> game_dirs;
148 QStringList recent_files; 161 QStringList recent_files;
149 QString language; 162 std::string language;
150 163
151 QString theme; 164 std::string theme;
152 165
153 // Shortcut name <Shortcut, context> 166 // Shortcut name <Shortcut, context>
154 std::vector<Shortcut> shortcuts; 167 std::vector<Shortcut> shortcuts;
@@ -202,6 +215,54 @@ extern Values values;
202 215
203u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); 216u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
204 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
205} // namespace UISettings 253} // namespace UISettings
206 254
207Q_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 f2854c8ec..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"
@@ -42,7 +45,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) {
42 return circle_pixmap; 45 return circle_pixmap;
43} 46}
44 47
45bool SaveIconToFile(const std::string_view path, const QImage& image) { 48bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) {
46#if defined(WIN32) 49#if defined(WIN32)
47#pragma pack(push, 2) 50#pragma pack(push, 2)
48 struct IconDir { 51 struct IconDir {
@@ -73,7 +76,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
73 .id_count = static_cast<WORD>(scale_sizes.size()), 76 .id_count = static_cast<WORD>(scale_sizes.size()),
74 }; 77 };
75 78
76 Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, 79 Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write,
77 Common::FS::FileType::BinaryFile); 80 Common::FS::FileType::BinaryFile);
78 if (!icon_file.IsOpen()) { 81 if (!icon_file.IsOpen()) {
79 return false; 82 return false;
@@ -135,6 +138,14 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) {
135 icon_file.Close(); 138 icon_file.Close();
136 139
137 return true; 140 return true;
141#elif defined(__linux__) || defined(__FreeBSD__)
142 // Convert and write the icon as a PNG
143 if (!image.save(QString::fromStdString(icon_path.string()))) {
144 LOG_ERROR(Frontend, "Could not write icon as PNG to file");
145 } else {
146 LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string());
147 }
148 return true;
138#else 149#else
139 return false; 150 return false;
140#endif 151#endif
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h
index 09c14ce3f..4094cf6c2 100644
--- a/src/yuzu/util/util.h
+++ b/src/yuzu/util/util.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <filesystem>
6#include <QFont> 7#include <QFont>
7#include <QString> 8#include <QString>
8 9
@@ -25,4 +26,4 @@
25 * @param image The image to save 26 * @param image The image to save
26 * @return bool If the operation succeeded 27 * @return bool If the operation succeeded
27 */ 28 */
28[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); 29[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image);
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..0416d5951 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"
@@ -300,7 +301,7 @@ int main(int argc, char** argv) {
300 } 301 }
301 } 302 }
302 303
303 Config config{config_path}; 304 SdlConfig config{config_path};
304 305
305 // apply the log_filter setting 306 // apply the log_filter setting
306 // the logger was initialized before and doesn't pick up the filter on its own 307 // the logger was initialized before and doesn't pick up the filter on its own