summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/build.gradle.kts2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt9
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt75
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt2
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt124
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt987
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt4
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt188
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt19
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt13
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DirectoryInitialization.kt175
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt50
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NativeConfig.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt37
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt38
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.cpp9
-rw-r--r--src/android/app/src/main/jni/android_common/android_common.h3
-rw-r--r--src/android/app/src/main/jni/android_config.cpp73
-rw-r--r--src/android/app/src/main/jni/android_config.h3
-rw-r--r--src/android/app/src/main/jni/android_settings.h30
-rw-r--r--src/android/app/src/main/jni/id_cache.cpp79
-rw-r--r--src/android/app/src/main/jni/id_cache.h12
-rw-r--r--src/android/app/src/main/jni/native_config.cpp70
-rw-r--r--src/android/app/src/main/res/layout/list_item_setting_switch.xml2
-rw-r--r--src/android/app/src/main/res/menu/menu_overlay_options.xml5
-rw-r--r--src/android/app/src/main/res/values/arrays.xml39
-rw-r--r--src/android/app/src/main/res/values/integers.xml204
-rw-r--r--src/android/app/src/main/res/values/strings.xml15
-rw-r--r--src/android/build.gradle.kts2
-rw-r--r--src/audio_core/device/device_session.cpp4
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/assert.cpp3
-rw-r--r--src/common/heap_tracker.cpp281
-rw-r--r--src/common/heap_tracker.h98
-rw-r--r--src/common/host_memory.cpp10
-rw-r--r--src/common/host_memory.h11
-rw-r--r--src/common/logging/backend.cpp17
-rw-r--r--src/common/logging/backend.h3
-rw-r--r--src/common/ring_buffer.h2
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings_common.h1
-rw-r--r--src/core/CMakeLists.txt23
-rw-r--r--src/core/arm/arm_interface.cpp2
-rw-r--r--src/core/arm/arm_interface.h2
-rw-r--r--src/core/arm/debug.cpp14
-rw-r--r--src/core/arm/debug.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp49
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h20
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp11
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/arm/nce/arm_nce.cpp2
-rw-r--r--src/core/arm/nce/interpreter_visitor.cpp5
-rw-r--r--src/core/arm/nce/visitor_base.h6
-rw-r--r--src/core/core.cpp73
-rw-r--r--src/core/core.h13
-rw-r--r--src/core/core_timing.cpp48
-rw-r--r--src/core/core_timing.h27
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/program_metadata.cpp6
-rw-r--r--src/core/file_sys/program_metadata.h13
-rw-r--r--src/core/hid/hid_types.h1
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp19
-rw-r--r--src/core/hle/kernel/k_auto_object_container.cpp9
-rw-r--r--src/core/hle/kernel/k_auto_object_container.h31
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp6
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h3
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_table_base.cpp26
-rw-r--r--src/core/hle/kernel/k_page_table_base.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp56
-rw-r--r--src/core/hle/kernel/k_process.h15
-rw-r--r--src/core/hle/kernel/k_server_session.cpp1424
-rw-r--r--src/core/hle/kernel/k_server_session.h15
-rw-r--r--src/core/hle/kernel/k_session.cpp3
-rw-r--r--src/core/hle/kernel/k_thread.cpp11
-rw-r--r--src/core/hle/kernel/k_thread.h6
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h1
-rw-r--r--src/core/hle/kernel/kernel.cpp36
-rw-r--r--src/core/hle/kernel/kernel.h7
-rw-r--r--src/core/hle/kernel/message_buffer.h20
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp1
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp6
-rw-r--r--src/core/hle/kernel/svc_results.h2
-rw-r--r--src/core/hle/service/am/am.cpp12
-rw-r--r--src/core/hle/service/audio/audren_u.cpp6
-rw-r--r--src/core/hle/service/audio/hwopus.cpp16
-rw-r--r--src/core/hle/service/fatal/fatal.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.cpp18
-rw-r--r--src/core/hle/service/hid/controllers/applet_resource.h95
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/capture_button.h (renamed from src/core/hle/service/hid/controllers/stubbed.h)8
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/console_six_axis.h6
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp7
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h10
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.cpp64
-rw-r--r--src/core/hle/service/hid/controllers/debug_mouse.h34
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h6
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/digitizer.h27
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h5
-rw-r--r--src/core/hle/service/hid/controllers/home_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/home_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h6
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h5
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1003
-rw-r--r--src/core/hle/service/hid/controllers/npad.h166
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_data.cpp228
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_data.h88
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_resource.cpp685
-rw-r--r--src/core/hle/service/hid/controllers/npad/npad_resource.h132
-rw-r--r--src/core/hle/service/hid/controllers/shared_memory_holder.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/six_axis.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.cpp39
-rw-r--r--src/core/hle/service/hid/controllers/sleep_button.h27
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp31
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp17
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h4
-rw-r--r--src/core/hle/service/hid/controllers/types/npad_types.h3
-rw-r--r--src/core/hle/service/hid/controllers/types/shared_memory_format.h (renamed from src/core/hle/service/hid/controllers/shared_memory_format.h)2
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.cpp38
-rw-r--r--src/core/hle/service/hid/controllers/unique_pad.h27
-rw-r--r--src/core/hle/service/hid/errors.h32
-rw-r--r--src/core/hle/service/hid/hid.cpp1
-rw-r--r--src/core/hle/service/hid/hid_server.cpp233
-rw-r--r--src/core/hle/service/hid/hid_system_server.cpp38
-rw-r--r--src/core/hle/service/hid/hid_util.h8
-rw-r--r--src/core/hle/service/hid/hidbus.cpp11
-rw-r--r--src/core/hle/service/hid/hidbus.h2
-rw-r--r--src/core/hle/service/hid/irs.cpp8
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp161
-rw-r--r--src/core/hle/service/hid/resource_manager.h28
-rw-r--r--src/core/hle/service/hle_ipc.cpp20
-rw-r--r--src/core/hle/service/hle_ipc.h20
-rw-r--r--src/core/hle/service/ipc_helpers.h4
-rw-r--r--src/core/hle/service/jit/jit.cpp69
-rw-r--r--src/core/hle/service/jit/jit_code_memory.cpp54
-rw-r--r--src/core/hle/service/jit/jit_code_memory.h49
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp8
-rw-r--r--src/core/hle/service/ro/ro.cpp12
-rw-r--r--src/core/hle/service/server_manager.cpp21
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/set/set_sys.cpp12
-rw-r--r--src/core/hle/service/set/set_sys.h1
-rw-r--r--src/core/hle/service/sm/sm.cpp36
-rw-r--r--src/core/hle/service/sm/sm.h8
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp7
-rw-r--r--src/core/memory.cpp114
-rw-r--r--src/core/memory.h10
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/memory/cheat_engine.h2
-rw-r--r--src/core/tools/freezer.cpp17
-rw-r--r--src/core/tools/freezer.h2
-rw-r--r--src/frontend_common/config.cpp184
-rw-r--r--src/frontend_common/config.h39
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp5
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp22
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp81
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h2
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp6
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp12
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp25
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h107
-rw-r--r--src/tests/common/host_memory.cpp99
-rw-r--r--src/tests/core/core_timing.cpp14
-rw-r--r--src/video_core/engines/maxwell_3d.cpp24
-rw-r--r--src/video_core/engines/maxwell_dma.cpp4
-rw-r--r--src/video_core/macro/macro_hle.cpp7
-rw-r--r--src/video_core/query_cache.h39
-rw-r--r--src/video_core/rasterizer_interface.h8
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h5
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp54
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp63
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_present_manager.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/shader_environment.cpp35
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/video_core/texture_cache/decode_bc.cpp50
-rw-r--r--src/video_core/texture_cache/decode_bc.h2
-rw-r--r--src/video_core/texture_cache/util.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp21
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp94
-rw-r--r--src/yuzu/configuration/configure_graphics.h4
-rw-r--r--src/yuzu/configuration/qt_config.cpp69
-rw-r--r--src/yuzu/main.cpp4
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/multiplayer/direct_connect.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp3
-rw-r--r--src/yuzu_cmd/sdl_config.cpp26
223 files changed, 7289 insertions, 3004 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts
index f763c657e..53aafa08c 100644
--- a/src/android/app/build.gradle.kts
+++ b/src/android/app/build.gradle.kts
@@ -10,7 +10,7 @@ plugins {
10 id("com.android.application") 10 id("com.android.application")
11 id("org.jetbrains.kotlin.android") 11 id("org.jetbrains.kotlin.android")
12 id("kotlin-parcelize") 12 id("kotlin-parcelize")
13 kotlin("plugin.serialization") version "1.8.21" 13 kotlin("plugin.serialization") version "1.9.20"
14 id("androidx.navigation.safeargs.kotlin") 14 id("androidx.navigation.safeargs.kotlin")
15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0" 15 id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
16} 16}
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 9b08f008d..93c8ce922 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -49,6 +49,7 @@ import org.yuzu.yuzu_emu.utils.ForegroundService
49import org.yuzu.yuzu_emu.utils.InputHandler 49import org.yuzu.yuzu_emu.utils.InputHandler
50import org.yuzu.yuzu_emu.utils.Log 50import org.yuzu.yuzu_emu.utils.Log
51import org.yuzu.yuzu_emu.utils.MemoryUtil 51import org.yuzu.yuzu_emu.utils.MemoryUtil
52import org.yuzu.yuzu_emu.utils.NativeConfig
52import org.yuzu.yuzu_emu.utils.NfcReader 53import org.yuzu.yuzu_emu.utils.NfcReader
53import org.yuzu.yuzu_emu.utils.ThemeHelper 54import org.yuzu.yuzu_emu.utils.ThemeHelper
54import java.text.NumberFormat 55import java.text.NumberFormat
@@ -170,6 +171,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
170 stopMotionSensorListener() 171 stopMotionSensorListener()
171 } 172 }
172 173
174 override fun onStop() {
175 super.onStop()
176 NativeConfig.saveGlobalConfig()
177 }
178
173 override fun onUserLeaveHint() { 179 override fun onUserLeaveHint() {
174 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { 180 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
175 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) { 181 if (BooleanSetting.PICTURE_IN_PICTURE.getBoolean() && !isInPictureInPictureMode) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
index 16f06cd0a..86bd33672 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt
@@ -18,7 +18,14 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
18 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), 18 RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
19 RENDERER_DEBUG("debug"), 19 RENDERER_DEBUG("debug"),
20 PICTURE_IN_PICTURE("picture_in_picture"), 20 PICTURE_IN_PICTURE("picture_in_picture"),
21 USE_CUSTOM_RTC("custom_rtc_enabled"); 21 USE_CUSTOM_RTC("custom_rtc_enabled"),
22 BLACK_BACKGROUNDS("black_backgrounds"),
23 JOYSTICK_REL_CENTER("joystick_rel_center"),
24 DPAD_SLIDE("dpad_slide"),
25 HAPTIC_FEEDBACK("haptic_feedback"),
26 SHOW_PERFORMANCE_OVERLAY("show_performance_overlay"),
27 SHOW_INPUT_OVERLAY("show_input_overlay"),
28 TOUCHSCREEN("touchscreen");
22 29
23 override fun getBoolean(needsGlobal: Boolean): Boolean = 30 override fun getBoolean(needsGlobal: Boolean): Boolean =
24 NativeConfig.getBoolean(key, needsGlobal) 31 NativeConfig.getBoolean(key, needsGlobal)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
index 21e4e1afd..16fb87614 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/IntSetting.kt
@@ -18,7 +18,12 @@ enum class IntSetting(override val key: String) : AbstractIntSetting {
18 RENDERER_ANTI_ALIASING("anti_aliasing"), 18 RENDERER_ANTI_ALIASING("anti_aliasing"),
19 RENDERER_SCREEN_LAYOUT("screen_layout"), 19 RENDERER_SCREEN_LAYOUT("screen_layout"),
20 RENDERER_ASPECT_RATIO("aspect_ratio"), 20 RENDERER_ASPECT_RATIO("aspect_ratio"),
21 AUDIO_OUTPUT_ENGINE("output_engine"); 21 AUDIO_OUTPUT_ENGINE("output_engine"),
22 MAX_ANISOTROPY("max_anisotropy"),
23 THEME("theme"),
24 THEME_MODE("theme_mode"),
25 OVERLAY_SCALE("control_scale"),
26 OVERLAY_OPACITY("control_opacity");
22 27
23 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal) 28 override fun getInt(needsGlobal: Boolean): Int = NativeConfig.getInt(key, needsGlobal)
24 29
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 9551fc05e..fee80bb21 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
@@ -15,18 +15,10 @@ object Settings {
15 SECTION_DEBUG(R.string.preferences_debug); 15 SECTION_DEBUG(R.string.preferences_debug);
16 } 16 }
17 17
18 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
18 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 19 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
19 20
20 const val PREF_OVERLAY_VERSION = "OverlayVersion" 21 // Deprecated input overlay preference keys
21 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
22 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
23 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
24 val overlayLayoutPrefs = listOf(
25 PREF_LANDSCAPE_OVERLAY_VERSION,
26 PREF_PORTRAIT_OVERLAY_VERSION,
27 PREF_FOLDABLE_OVERLAY_VERSION
28 )
29
30 const val PREF_CONTROL_SCALE = "controlScale" 22 const val PREF_CONTROL_SCALE = "controlScale"
31 const val PREF_CONTROL_OPACITY = "controlOpacity" 23 const val PREF_CONTROL_OPACITY = "controlOpacity"
32 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 24 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
@@ -47,23 +39,12 @@ object Settings {
47 const val PREF_BUTTON_STICK_R = "buttonToggle14" 39 const val PREF_BUTTON_STICK_R = "buttonToggle14"
48 const val PREF_BUTTON_HOME = "buttonToggle15" 40 const val PREF_BUTTON_HOME = "buttonToggle15"
49 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" 41 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
50
51 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" 42 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
52 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" 43 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
53 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics" 44 const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
54 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps" 45 const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
55 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay" 46 const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
56
57 const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch"
58 const val PREF_THEME = "Theme"
59 const val PREF_THEME_MODE = "ThemeMode"
60 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
61
62 val overlayPreferences = listOf( 47 val overlayPreferences = listOf(
63 PREF_OVERLAY_VERSION,
64 PREF_CONTROL_SCALE,
65 PREF_CONTROL_OPACITY,
66 PREF_TOUCH_ENABLED,
67 PREF_BUTTON_A, 48 PREF_BUTTON_A,
68 PREF_BUTTON_B, 49 PREF_BUTTON_B,
69 PREF_BUTTON_X, 50 PREF_BUTTON_X,
@@ -83,7 +64,33 @@ object Settings {
83 PREF_BUTTON_STICK_R 64 PREF_BUTTON_STICK_R
84 ) 65 )
85 66
86 const val LayoutOption_Unspecified = 0 67 // Deprecated layout preference keys
87 const val LayoutOption_MobilePortrait = 4 68 const val PREF_LANDSCAPE_SUFFIX = "_Landscape"
88 const val LayoutOption_MobileLandscape = 5 69 const val PREF_PORTRAIT_SUFFIX = "_Portrait"
70 const val PREF_FOLDABLE_SUFFIX = "_Foldable"
71 val overlayLayoutSuffixes = listOf(
72 PREF_LANDSCAPE_SUFFIX,
73 PREF_PORTRAIT_SUFFIX,
74 PREF_FOLDABLE_SUFFIX
75 )
76
77 // Deprecated theme preference keys
78 const val PREF_THEME = "Theme"
79 const val PREF_THEME_MODE = "ThemeMode"
80 const val PREF_BLACK_BACKGROUNDS = "BlackBackgrounds"
81
82 enum class EmulationOrientation(val int: Int) {
83 Unspecified(0),
84 SensorLandscape(5),
85 Landscape(1),
86 ReverseLandscape(2),
87 SensorPortrait(6),
88 Portrait(4),
89 ReversePortrait(3);
90
91 companion object {
92 fun from(int: Int): EmulationOrientation =
93 entries.firstOrNull { it.int == int } ?: Unspecified
94 }
95 }
89} 96}
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 2e97aee2c..12f7aa1ab 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
@@ -245,6 +245,15 @@ abstract class SettingsItem(
245 ) 245 )
246 put( 246 put(
247 SingleChoiceSetting( 247 SingleChoiceSetting(
248 IntSetting.MAX_ANISOTROPY,
249 R.string.anisotropic_filtering,
250 R.string.anisotropic_filtering_description,
251 R.array.anisoEntries,
252 R.array.anisoValues
253 )
254 )
255 put(
256 SingleChoiceSetting(
248 IntSetting.AUDIO_OUTPUT_ENGINE, 257 IntSetting.AUDIO_OUTPUT_ENGINE,
249 R.string.audio_output_engine, 258 R.string.audio_output_engine,
250 0, 259 0,
@@ -298,6 +307,7 @@ abstract class SettingsItem(
298 307
299 override val key: String = FASTMEM_COMBINED 308 override val key: String = FASTMEM_COMBINED
300 override val isRuntimeModifiable: Boolean = false 309 override val isRuntimeModifiable: Boolean = false
310 override val pairedSettingKey = BooleanSetting.CPU_DEBUG_MODE.key
301 override val defaultValue: Boolean = true 311 override val defaultValue: Boolean = true
302 override val isSwitchable: Boolean = true 312 override val isSwitchable: Boolean = true
303 override var global: Boolean 313 override var global: Boolean
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 a7e965589..2ad2f4966 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,10 +3,8 @@
3 3
4package org.yuzu.yuzu_emu.features.settings.ui 4package org.yuzu.yuzu_emu.features.settings.ui
5 5
6import android.content.SharedPreferences
7import android.os.Build 6import android.os.Build
8import android.widget.Toast 7import android.widget.Toast
9import androidx.preference.PreferenceManager
10import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
11import org.yuzu.yuzu_emu.R 9import org.yuzu.yuzu_emu.R
12import org.yuzu.yuzu_emu.YuzuApplication 10import org.yuzu.yuzu_emu.YuzuApplication
@@ -29,9 +27,6 @@ class SettingsFragmentPresenter(
29) { 27) {
30 private var settingsList = ArrayList<SettingsItem>() 28 private var settingsList = ArrayList<SettingsItem>()
31 29
32 private val preferences: SharedPreferences
33 get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
34
35 // Extension for altering settings list based on each setting's properties 30 // Extension for altering settings list based on each setting's properties
36 fun ArrayList<SettingsItem>.add(key: String) { 31 fun ArrayList<SettingsItem>.add(key: String) {
37 val item = SettingsItem.settingsItems[key]!! 32 val item = SettingsItem.settingsItems[key]!!
@@ -149,6 +144,7 @@ class SettingsFragmentPresenter(
149 add(IntSetting.RENDERER_VSYNC.key) 144 add(IntSetting.RENDERER_VSYNC.key)
150 add(IntSetting.RENDERER_SCALING_FILTER.key) 145 add(IntSetting.RENDERER_SCALING_FILTER.key)
151 add(IntSetting.RENDERER_ANTI_ALIASING.key) 146 add(IntSetting.RENDERER_ANTI_ALIASING.key)
147 add(IntSetting.MAX_ANISOTROPY.key)
152 add(IntSetting.RENDERER_SCREEN_LAYOUT.key) 148 add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
153 add(IntSetting.RENDERER_ASPECT_RATIO.key) 149 add(IntSetting.RENDERER_ASPECT_RATIO.key)
154 add(BooleanSetting.PICTURE_IN_PICTURE.key) 150 add(BooleanSetting.PICTURE_IN_PICTURE.key)
@@ -169,25 +165,19 @@ class SettingsFragmentPresenter(
169 private fun addThemeSettings(sl: ArrayList<SettingsItem>) { 165 private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
170 sl.apply { 166 sl.apply {
171 val theme: AbstractIntSetting = object : AbstractIntSetting { 167 val theme: AbstractIntSetting = object : AbstractIntSetting {
172 override fun getInt(needsGlobal: Boolean): Int = 168 override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME.getInt()
173 preferences.getInt(Settings.PREF_THEME, 0)
174
175 override fun setInt(value: Int) { 169 override fun setInt(value: Int) {
176 preferences.edit() 170 IntSetting.THEME.setInt(value)
177 .putInt(Settings.PREF_THEME, value)
178 .apply()
179 settingsViewModel.setShouldRecreate(true) 171 settingsViewModel.setShouldRecreate(true)
180 } 172 }
181 173
182 override val key: String = Settings.PREF_THEME 174 override val key: String = IntSetting.THEME.key
183 override val isRuntimeModifiable: Boolean = false 175 override val isRuntimeModifiable: Boolean = IntSetting.THEME.isRuntimeModifiable
184 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() 176 override fun getValueAsString(needsGlobal: Boolean): String =
185 override val defaultValue: Int = 0 177 IntSetting.THEME.getValueAsString()
186 override fun reset() { 178
187 preferences.edit() 179 override val defaultValue: Int = IntSetting.THEME.defaultValue
188 .putInt(Settings.PREF_THEME, defaultValue) 180 override fun reset() = IntSetting.THEME.setInt(defaultValue)
189 .apply()
190 }
191 } 181 }
192 182
193 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 183 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -213,24 +203,22 @@ class SettingsFragmentPresenter(
213 } 203 }
214 204
215 val themeMode: AbstractIntSetting = object : AbstractIntSetting { 205 val themeMode: AbstractIntSetting = object : AbstractIntSetting {
216 override fun getInt(needsGlobal: Boolean): Int = 206 override fun getInt(needsGlobal: Boolean): Int = IntSetting.THEME_MODE.getInt()
217 preferences.getInt(Settings.PREF_THEME_MODE, -1)
218
219 override fun setInt(value: Int) { 207 override fun setInt(value: Int) {
220 preferences.edit() 208 IntSetting.THEME_MODE.setInt(value)
221 .putInt(Settings.PREF_THEME_MODE, value)
222 .apply()
223 settingsViewModel.setShouldRecreate(true) 209 settingsViewModel.setShouldRecreate(true)
224 } 210 }
225 211
226 override val key: String = Settings.PREF_THEME_MODE 212 override val key: String = IntSetting.THEME_MODE.key
227 override val isRuntimeModifiable: Boolean = false 213 override val isRuntimeModifiable: Boolean =
228 override fun getValueAsString(needsGlobal: Boolean): String = getInt().toString() 214 IntSetting.THEME_MODE.isRuntimeModifiable
229 override val defaultValue: Int = -1 215
216 override fun getValueAsString(needsGlobal: Boolean): String =
217 IntSetting.THEME_MODE.getValueAsString()
218
219 override val defaultValue: Int = IntSetting.THEME_MODE.defaultValue
230 override fun reset() { 220 override fun reset() {
231 preferences.edit() 221 IntSetting.THEME_MODE.setInt(defaultValue)
232 .putInt(Settings.PREF_BLACK_BACKGROUNDS, defaultValue)
233 .apply()
234 settingsViewModel.setShouldRecreate(true) 222 settingsViewModel.setShouldRecreate(true)
235 } 223 }
236 } 224 }
@@ -247,25 +235,24 @@ class SettingsFragmentPresenter(
247 235
248 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting { 236 val blackBackgrounds: AbstractBooleanSetting = object : AbstractBooleanSetting {
249 override fun getBoolean(needsGlobal: Boolean): Boolean = 237 override fun getBoolean(needsGlobal: Boolean): Boolean =
250 preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) 238 BooleanSetting.BLACK_BACKGROUNDS.getBoolean()
251 239
252 override fun setBoolean(value: Boolean) { 240 override fun setBoolean(value: Boolean) {
253 preferences.edit() 241 BooleanSetting.BLACK_BACKGROUNDS.setBoolean(value)
254 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, value)
255 .apply()
256 settingsViewModel.setShouldRecreate(true) 242 settingsViewModel.setShouldRecreate(true)
257 } 243 }
258 244
259 override val key: String = Settings.PREF_BLACK_BACKGROUNDS 245 override val key: String = BooleanSetting.BLACK_BACKGROUNDS.key
260 override val isRuntimeModifiable: Boolean = false 246 override val isRuntimeModifiable: Boolean =
247 BooleanSetting.BLACK_BACKGROUNDS.isRuntimeModifiable
248
261 override fun getValueAsString(needsGlobal: Boolean): String = 249 override fun getValueAsString(needsGlobal: Boolean): String =
262 getBoolean().toString() 250 BooleanSetting.BLACK_BACKGROUNDS.getValueAsString()
263 251
264 override val defaultValue: Boolean = false 252 override val defaultValue: Boolean = BooleanSetting.BLACK_BACKGROUNDS.defaultValue
265 override fun reset() { 253 override fun reset() {
266 preferences.edit() 254 BooleanSetting.BLACK_BACKGROUNDS
267 .putBoolean(Settings.PREF_BLACK_BACKGROUNDS, defaultValue) 255 .setBoolean(BooleanSetting.BLACK_BACKGROUNDS.defaultValue)
268 .apply()
269 settingsViewModel.setShouldRecreate(true) 256 settingsViewModel.setShouldRecreate(true)
270 } 257 }
271 } 258 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 0dce8ad8d..816336820 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -149,7 +149,7 @@ class AddonsFragment : Fragment() {
149 } 149 }
150 150
151 val isValid = externalAddonDirectory.listFiles() 151 val isValid = externalAddonDirectory.listFiles()
152 .any { AddonUtil.validAddonDirectories.contains(it.name) } 152 .any { AddonUtil.validAddonDirectories.contains(it.name?.lowercase()) }
153 val errorMessage = MessageDialogFragment.newInstance( 153 val errorMessage = MessageDialogFragment.newInstance(
154 requireActivity(), 154 requireActivity(),
155 titleId = R.string.invalid_directory, 155 titleId = R.string.invalid_directory,
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index d7b38f62d..9efc1705d 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -7,7 +7,6 @@ import android.annotation.SuppressLint
7import android.app.AlertDialog 7import android.app.AlertDialog
8import android.content.Context 8import android.content.Context
9import android.content.DialogInterface 9import android.content.DialogInterface
10import android.content.SharedPreferences
11import android.content.pm.ActivityInfo 10import android.content.pm.ActivityInfo
12import android.content.res.Configuration 11import android.content.res.Configuration
13import android.net.Uri 12import android.net.Uri
@@ -33,7 +32,6 @@ import androidx.lifecycle.lifecycleScope
33import androidx.lifecycle.repeatOnLifecycle 32import androidx.lifecycle.repeatOnLifecycle
34import androidx.navigation.findNavController 33import androidx.navigation.findNavController
35import androidx.navigation.fragment.navArgs 34import androidx.navigation.fragment.navArgs
36import androidx.preference.PreferenceManager
37import androidx.window.layout.FoldingFeature 35import androidx.window.layout.FoldingFeature
38import androidx.window.layout.WindowInfoTracker 36import androidx.window.layout.WindowInfoTracker
39import androidx.window.layout.WindowLayoutInfo 37import androidx.window.layout.WindowLayoutInfo
@@ -46,22 +44,23 @@ import kotlinx.coroutines.launch
46import org.yuzu.yuzu_emu.HomeNavigationDirections 44import org.yuzu.yuzu_emu.HomeNavigationDirections
47import org.yuzu.yuzu_emu.NativeLibrary 45import org.yuzu.yuzu_emu.NativeLibrary
48import org.yuzu.yuzu_emu.R 46import org.yuzu.yuzu_emu.R
49import org.yuzu.yuzu_emu.YuzuApplication
50import org.yuzu.yuzu_emu.activities.EmulationActivity 47import org.yuzu.yuzu_emu.activities.EmulationActivity
51import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding 48import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
52import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding 49import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
50import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
53import org.yuzu.yuzu_emu.features.settings.model.IntSetting 51import org.yuzu.yuzu_emu.features.settings.model.IntSetting
54import org.yuzu.yuzu_emu.features.settings.model.Settings 52import org.yuzu.yuzu_emu.features.settings.model.Settings
53import org.yuzu.yuzu_emu.features.settings.model.Settings.EmulationOrientation
55import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile 54import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
56import org.yuzu.yuzu_emu.model.DriverViewModel 55import org.yuzu.yuzu_emu.model.DriverViewModel
57import org.yuzu.yuzu_emu.model.Game 56import org.yuzu.yuzu_emu.model.Game
58import org.yuzu.yuzu_emu.model.EmulationViewModel 57import org.yuzu.yuzu_emu.model.EmulationViewModel
59import org.yuzu.yuzu_emu.overlay.InputOverlay 58import org.yuzu.yuzu_emu.overlay.model.OverlayControl
59import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
60import org.yuzu.yuzu_emu.utils.* 60import org.yuzu.yuzu_emu.utils.*
61import java.lang.NullPointerException 61import java.lang.NullPointerException
62 62
63class EmulationFragment : Fragment(), SurfaceHolder.Callback { 63class EmulationFragment : Fragment(), SurfaceHolder.Callback {
64 private lateinit var preferences: SharedPreferences
65 private lateinit var emulationState: EmulationState 64 private lateinit var emulationState: EmulationState
66 private var emulationActivity: EmulationActivity? = null 65 private var emulationActivity: EmulationActivity? = null
67 private var perfStatsUpdater: (() -> Unit)? = null 66 private var perfStatsUpdater: (() -> Unit)? = null
@@ -101,6 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
101 */ 100 */
102 override fun onCreate(savedInstanceState: Bundle?) { 101 override fun onCreate(savedInstanceState: Bundle?) {
103 super.onCreate(savedInstanceState) 102 super.onCreate(savedInstanceState)
103 updateOrientation()
104 104
105 val intentUri: Uri? = requireActivity().intent.data 105 val intentUri: Uri? = requireActivity().intent.data
106 var intentGame: Game? = null 106 var intentGame: Game? = null
@@ -141,7 +141,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
141 141
142 // So this fragment doesn't restart on configuration changes; i.e. rotation. 142 // So this fragment doesn't restart on configuration changes; i.e. rotation.
143 retainInstance = true 143 retainInstance = true
144 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
145 emulationState = EmulationState(game.path) 144 emulationState = EmulationState(game.path)
146 } 145 }
147 146
@@ -382,24 +381,25 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
382 } 381 }
383 382
384 updateScreenLayout() 383 updateScreenLayout()
384 val showInputOverlay = BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
385 if (emulationActivity?.isInPictureInPictureMode == true) { 385 if (emulationActivity?.isInPictureInPictureMode == true) {
386 if (binding.drawerLayout.isOpen) { 386 if (binding.drawerLayout.isOpen) {
387 binding.drawerLayout.close() 387 binding.drawerLayout.close()
388 } 388 }
389 if (EmulationMenuSettings.showOverlay) { 389 if (showInputOverlay) {
390 binding.surfaceInputOverlay.visibility = View.INVISIBLE 390 binding.surfaceInputOverlay.visibility = View.INVISIBLE
391 } 391 }
392 } else { 392 } else {
393 if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) { 393 if (showInputOverlay && emulationViewModel.emulationStarted.value) {
394 binding.surfaceInputOverlay.visibility = View.VISIBLE 394 binding.surfaceInputOverlay.visibility = View.VISIBLE
395 } else { 395 } else {
396 binding.surfaceInputOverlay.visibility = View.INVISIBLE 396 binding.surfaceInputOverlay.visibility = View.INVISIBLE
397 } 397 }
398 if (!isInFoldableLayout) { 398 if (!isInFoldableLayout) {
399 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 399 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
400 binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT 400 binding.surfaceInputOverlay.layout = OverlayLayout.Portrait
401 } else { 401 } else {
402 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE 402 binding.surfaceInputOverlay.layout = OverlayLayout.Landscape
403 } 403 }
404 } 404 }
405 } 405 }
@@ -423,17 +423,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
423 } 423 }
424 424
425 private fun resetInputOverlay() { 425 private fun resetInputOverlay() {
426 preferences.edit() 426 IntSetting.OVERLAY_SCALE.reset()
427 .remove(Settings.PREF_CONTROL_SCALE) 427 IntSetting.OVERLAY_OPACITY.reset()
428 .remove(Settings.PREF_CONTROL_OPACITY)
429 .apply()
430 binding.surfaceInputOverlay.post { 428 binding.surfaceInputOverlay.post {
431 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() 429 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
432 } 430 }
433 } 431 }
434 432
435 private fun updateShowFpsOverlay() { 433 private fun updateShowFpsOverlay() {
436 if (EmulationMenuSettings.showFps) { 434 if (BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()) {
437 val SYSTEM_FPS = 0 435 val SYSTEM_FPS = 0
438 val FPS = 1 436 val FPS = 1
439 val FRAMETIME = 2 437 val FRAMETIME = 2
@@ -462,13 +460,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
462 @SuppressLint("SourceLockedOrientationActivity") 460 @SuppressLint("SourceLockedOrientationActivity")
463 private fun updateOrientation() { 461 private fun updateOrientation() {
464 emulationActivity?.let { 462 emulationActivity?.let {
465 it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.getInt()) { 463 val orientationSetting =
466 Settings.LayoutOption_MobileLandscape -> 464 EmulationOrientation.from(IntSetting.RENDERER_SCREEN_LAYOUT.getInt())
465 it.requestedOrientation = when (orientationSetting) {
466 EmulationOrientation.Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
467 EmulationOrientation.SensorLandscape ->
467 ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 468 ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
468 Settings.LayoutOption_MobilePortrait -> 469
469 ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT 470 EmulationOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
470 Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 471 EmulationOrientation.ReverseLandscape ->
471 else -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE 472 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
473
474 EmulationOrientation.SensorPortrait ->
475 ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
476
477 EmulationOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
478 EmulationOrientation.ReversePortrait ->
479 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
472 } 480 }
473 } 481 }
474 } 482 }
@@ -496,7 +504,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
496 binding.inGameMenu.layoutParams.height = it.bounds.bottom 504 binding.inGameMenu.layoutParams.height = it.bounds.bottom
497 505
498 isInFoldableLayout = true 506 isInFoldableLayout = true
499 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE 507 binding.surfaceInputOverlay.layout = OverlayLayout.Foldable
500 } 508 }
501 } 509 }
502 it.isSeparating 510 it.isSeparating
@@ -535,18 +543,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
535 popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu) 543 popup.menuInflater.inflate(R.menu.menu_overlay_options, popup.menu)
536 544
537 popup.menu.apply { 545 popup.menu.apply {
538 findItem(R.id.menu_toggle_fps).isChecked = EmulationMenuSettings.showFps 546 findItem(R.id.menu_toggle_fps).isChecked =
539 findItem(R.id.menu_rel_stick_center).isChecked = EmulationMenuSettings.joystickRelCenter 547 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
540 findItem(R.id.menu_dpad_slide).isChecked = EmulationMenuSettings.dpadSlide 548 findItem(R.id.menu_rel_stick_center).isChecked =
541 findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay 549 BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()
542 findItem(R.id.menu_haptics).isChecked = EmulationMenuSettings.hapticFeedback 550 findItem(R.id.menu_dpad_slide).isChecked = BooleanSetting.DPAD_SLIDE.getBoolean()
551 findItem(R.id.menu_show_overlay).isChecked =
552 BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()
553 findItem(R.id.menu_haptics).isChecked = BooleanSetting.HAPTIC_FEEDBACK.getBoolean()
554 findItem(R.id.menu_touchscreen).isChecked = BooleanSetting.TOUCHSCREEN.getBoolean()
543 } 555 }
544 556
545 popup.setOnMenuItemClickListener { 557 popup.setOnMenuItemClickListener {
546 when (it.itemId) { 558 when (it.itemId) {
547 R.id.menu_toggle_fps -> { 559 R.id.menu_toggle_fps -> {
548 it.isChecked = !it.isChecked 560 it.isChecked = !it.isChecked
549 EmulationMenuSettings.showFps = it.isChecked 561 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(it.isChecked)
550 updateShowFpsOverlay() 562 updateShowFpsOverlay()
551 true 563 true
552 } 564 }
@@ -564,11 +576,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
564 } 576 }
565 577
566 R.id.menu_toggle_controls -> { 578 R.id.menu_toggle_controls -> {
567 val preferences = 579 val overlayControlData = NativeConfig.getOverlayControlData()
568 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 580 val optionsArray = BooleanArray(overlayControlData.size)
569 val optionsArray = BooleanArray(Settings.overlayPreferences.size) 581 overlayControlData.forEachIndexed { i, _ ->
570 Settings.overlayPreferences.forEachIndexed { i, _ -> 582 optionsArray[i] = overlayControlData.firstOrNull { data ->
571 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15) 583 OverlayControl.entries[i].id == data.id
584 }?.enabled == true
572 } 585 }
573 586
574 val dialog = MaterialAlertDialogBuilder(requireContext()) 587 val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -577,11 +590,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
577 R.array.gamepadButtons, 590 R.array.gamepadButtons,
578 optionsArray 591 optionsArray
579 ) { _, indexSelected, isChecked -> 592 ) { _, indexSelected, isChecked ->
580 preferences.edit() 593 overlayControlData.firstOrNull { data ->
581 .putBoolean("buttonToggle$indexSelected", isChecked) 594 OverlayControl.entries[indexSelected].id == data.id
582 .apply() 595 }?.enabled = isChecked
583 } 596 }
584 .setPositiveButton(android.R.string.ok) { _, _ -> 597 .setPositiveButton(android.R.string.ok) { _, _ ->
598 NativeConfig.setOverlayControlData(overlayControlData)
599 NativeConfig.saveGlobalConfig()
585 binding.surfaceInputOverlay.refreshControls() 600 binding.surfaceInputOverlay.refreshControls()
586 } 601 }
587 .setNegativeButton(android.R.string.cancel, null) 602 .setNegativeButton(android.R.string.cancel, null)
@@ -592,12 +607,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
592 dialog.getButton(AlertDialog.BUTTON_NEUTRAL) 607 dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
593 .setOnClickListener { 608 .setOnClickListener {
594 val isChecked = !optionsArray[0] 609 val isChecked = !optionsArray[0]
595 Settings.overlayPreferences.forEachIndexed { i, _ -> 610 overlayControlData.forEachIndexed { i, _ ->
596 optionsArray[i] = isChecked 611 optionsArray[i] = isChecked
597 dialog.listView.setItemChecked(i, isChecked) 612 dialog.listView.setItemChecked(i, isChecked)
598 preferences.edit() 613 overlayControlData[i].enabled = isChecked
599 .putBoolean("buttonToggle$i", isChecked)
600 .apply()
601 } 614 }
602 } 615 }
603 true 616 true
@@ -605,26 +618,32 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
605 618
606 R.id.menu_show_overlay -> { 619 R.id.menu_show_overlay -> {
607 it.isChecked = !it.isChecked 620 it.isChecked = !it.isChecked
608 EmulationMenuSettings.showOverlay = it.isChecked 621 BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(it.isChecked)
609 binding.surfaceInputOverlay.refreshControls() 622 binding.surfaceInputOverlay.refreshControls()
610 true 623 true
611 } 624 }
612 625
613 R.id.menu_rel_stick_center -> { 626 R.id.menu_rel_stick_center -> {
614 it.isChecked = !it.isChecked 627 it.isChecked = !it.isChecked
615 EmulationMenuSettings.joystickRelCenter = it.isChecked 628 BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(it.isChecked)
616 true 629 true
617 } 630 }
618 631
619 R.id.menu_dpad_slide -> { 632 R.id.menu_dpad_slide -> {
620 it.isChecked = !it.isChecked 633 it.isChecked = !it.isChecked
621 EmulationMenuSettings.dpadSlide = it.isChecked 634 BooleanSetting.DPAD_SLIDE.setBoolean(it.isChecked)
622 true 635 true
623 } 636 }
624 637
625 R.id.menu_haptics -> { 638 R.id.menu_haptics -> {
626 it.isChecked = !it.isChecked 639 it.isChecked = !it.isChecked
627 EmulationMenuSettings.hapticFeedback = it.isChecked 640 BooleanSetting.HAPTIC_FEEDBACK.setBoolean(it.isChecked)
641 true
642 }
643
644 R.id.menu_touchscreen -> {
645 it.isChecked = !it.isChecked
646 BooleanSetting.TOUCHSCREEN.setBoolean(it.isChecked)
628 true 647 true
629 } 648 }
630 649
@@ -644,7 +663,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
644 @SuppressLint("SourceLockedOrientationActivity") 663 @SuppressLint("SourceLockedOrientationActivity")
645 private fun startConfiguringControls() { 664 private fun startConfiguringControls() {
646 // Lock the current orientation to prevent editing inconsistencies 665 // Lock the current orientation to prevent editing inconsistencies
647 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) { 666 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
648 emulationActivity?.let { 667 emulationActivity?.let {
649 it.requestedOrientation = 668 it.requestedOrientation =
650 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { 669 if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
@@ -662,11 +681,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
662 binding.doneControlConfig.visibility = View.GONE 681 binding.doneControlConfig.visibility = View.GONE
663 binding.surfaceInputOverlay.setIsInEditMode(false) 682 binding.surfaceInputOverlay.setIsInEditMode(false)
664 // Unlock the orientation if it was locked for editing 683 // Unlock the orientation if it was locked for editing
665 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == Settings.LayoutOption_Unspecified) { 684 if (IntSetting.RENDERER_SCREEN_LAYOUT.getInt() == EmulationOrientation.Unspecified.int) {
666 emulationActivity?.let { 685 emulationActivity?.let {
667 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED 686 it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
668 } 687 }
669 } 688 }
689 NativeConfig.saveGlobalConfig()
670 } 690 }
671 691
672 @SuppressLint("SetTextI18n") 692 @SuppressLint("SetTextI18n")
@@ -675,7 +695,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
675 adjustBinding.apply { 695 adjustBinding.apply {
676 inputScaleSlider.apply { 696 inputScaleSlider.apply {
677 valueTo = 150F 697 valueTo = 150F
678 value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat() 698 value = IntSetting.OVERLAY_SCALE.getInt().toFloat()
679 addOnChangeListener( 699 addOnChangeListener(
680 Slider.OnChangeListener { _, value, _ -> 700 Slider.OnChangeListener { _, value, _ ->
681 inputScaleValue.text = "${value.toInt()}%" 701 inputScaleValue.text = "${value.toInt()}%"
@@ -685,7 +705,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
685 } 705 }
686 inputOpacitySlider.apply { 706 inputOpacitySlider.apply {
687 valueTo = 100F 707 valueTo = 100F
688 value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat() 708 value = IntSetting.OVERLAY_OPACITY.getInt().toFloat()
689 addOnChangeListener( 709 addOnChangeListener(
690 Slider.OnChangeListener { _, value, _ -> 710 Slider.OnChangeListener { _, value, _ ->
691 inputOpacityValue.text = "${value.toInt()}%" 711 inputOpacityValue.text = "${value.toInt()}%"
@@ -709,16 +729,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
709 } 729 }
710 730
711 private fun setControlScale(scale: Int) { 731 private fun setControlScale(scale: Int) {
712 preferences.edit() 732 IntSetting.OVERLAY_SCALE.setInt(scale)
713 .putInt(Settings.PREF_CONTROL_SCALE, scale)
714 .apply()
715 binding.surfaceInputOverlay.refreshControls() 733 binding.surfaceInputOverlay.refreshControls()
716 } 734 }
717 735
718 private fun setControlOpacity(opacity: Int) { 736 private fun setControlOpacity(opacity: Int) {
719 preferences.edit() 737 IntSetting.OVERLAY_OPACITY.setInt(opacity)
720 .putInt(Settings.PREF_CONTROL_OPACITY, opacity)
721 .apply()
722 binding.surfaceInputOverlay.refreshControls() 738 binding.surfaceInputOverlay.refreshControls()
723 } 739 }
724 740
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index b1d3c0040..b04d1208f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -445,7 +445,8 @@ class GamePropertiesFragment : Fragment() {
445 val zipResult = FileUtil.zipFromInternalStorage( 445 val zipResult = FileUtil.zipFromInternalStorage(
446 File(saveLocation), 446 File(saveLocation),
447 saveLocation.replaceAfterLast("/", ""), 447 saveLocation.replaceAfterLast("/", ""),
448 BufferedOutputStream(requireContext().contentResolver.openOutputStream(result)) 448 BufferedOutputStream(requireContext().contentResolver.openOutputStream(result)),
449 compression = false
449 ) 450 )
450 return@newInstance when (zipResult) { 451 return@newInstance when (zipResult) {
451 TaskState.Completed -> getString(R.string.export_success) 452 TaskState.Completed -> getString(R.string.export_success)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
index d19f20dc2..5ae05b5cc 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/GamesViewModel.kt
@@ -167,13 +167,14 @@ class GamesViewModel : ViewModel() {
167 } 167 }
168 } 168 }
169 169
170 fun onCloseGameFoldersFragment() = 170 fun onCloseGameFoldersFragment() {
171 NativeConfig.saveGlobalConfig()
171 viewModelScope.launch { 172 viewModelScope.launch {
172 withContext(Dispatchers.IO) { 173 withContext(Dispatchers.IO) {
173 NativeConfig.saveGlobalConfig()
174 getGameDirs(true) 174 getGameDirs(true)
175 } 175 }
176 } 176 }
177 }
177 178
178 private fun getGameDirs(reloadList: Boolean = false) { 179 private fun getGameDirs(reloadList: Boolean = false) {
179 val gameDirs = NativeConfig.getGameDirs() 180 val gameDirs = NativeConfig.getGameDirs()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
index a13faf3c7..c87486c90 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@@ -21,7 +21,6 @@ import android.view.View
21import android.view.View.OnTouchListener 21import android.view.View.OnTouchListener
22import android.view.WindowInsets 22import android.view.WindowInsets
23import androidx.core.content.ContextCompat 23import androidx.core.content.ContextCompat
24import androidx.preference.PreferenceManager
25import androidx.window.layout.WindowMetricsCalculator 24import androidx.window.layout.WindowMetricsCalculator
26import kotlin.math.max 25import kotlin.math.max
27import kotlin.math.min 26import kotlin.math.min
@@ -29,9 +28,12 @@ import org.yuzu.yuzu_emu.NativeLibrary
29import org.yuzu.yuzu_emu.NativeLibrary.ButtonType 28import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
30import org.yuzu.yuzu_emu.NativeLibrary.StickType 29import org.yuzu.yuzu_emu.NativeLibrary.StickType
31import org.yuzu.yuzu_emu.R 30import org.yuzu.yuzu_emu.R
32import org.yuzu.yuzu_emu.YuzuApplication 31import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
33import org.yuzu.yuzu_emu.features.settings.model.Settings 32import org.yuzu.yuzu_emu.features.settings.model.IntSetting
34import org.yuzu.yuzu_emu.utils.EmulationMenuSettings 33import org.yuzu.yuzu_emu.overlay.model.OverlayControl
34import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
35import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
36import org.yuzu.yuzu_emu.utils.NativeConfig
35 37
36/** 38/**
37 * Draws the interactive input overlay on top of the 39 * Draws the interactive input overlay on top of the
@@ -51,23 +53,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
51 53
52 private lateinit var windowInsets: WindowInsets 54 private lateinit var windowInsets: WindowInsets
53 55
54 var layout = LANDSCAPE 56 var layout = OverlayLayout.Landscape
55 57
56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 58 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
57 super.onLayout(changed, left, top, right, bottom) 59 super.onLayout(changed, left, top, right, bottom)
58 60
59 windowInsets = rootWindowInsets 61 windowInsets = rootWindowInsets
60 62
61 val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0) 63 val overlayControlData = NativeConfig.getOverlayControlData()
62 if (overlayVersion != OVERLAY_VERSION) { 64 if (overlayControlData.isEmpty()) {
63 resetAllLayouts() 65 populateDefaultConfig()
64 } else { 66 } else {
65 val layoutIndex = overlayLayouts.indexOf(layout) 67 checkForNewControls(overlayControlData)
66 val currentLayoutVersion =
67 preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
68 if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
69 resetCurrentLayout()
70 }
71 } 68 }
72 69
73 // Load the controls. 70 // Load the controls.
@@ -123,7 +120,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
123 } 120 }
124 121
125 for (dpad in overlayDpads) { 122 for (dpad in overlayDpads) {
126 if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide)) { 123 if (!dpad.updateStatus(event, BooleanSetting.DPAD_SLIDE.getBoolean())) {
127 continue 124 continue
128 } 125 }
129 NativeLibrary.onGamePadButtonEvent( 126 NativeLibrary.onGamePadButtonEvent(
@@ -174,7 +171,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
174 invalidate() 171 invalidate()
175 } 172 }
176 173
177 if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) { 174 if (!BooleanSetting.TOUCHSCREEN.getBoolean()) {
178 return true 175 return true
179 } 176 }
180 177
@@ -211,7 +208,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
211 } 208 }
212 209
213 private fun playHaptics(event: MotionEvent) { 210 private fun playHaptics(event: MotionEvent) {
214 if (EmulationMenuSettings.hapticFeedback) { 211 if (BooleanSetting.HAPTIC_FEEDBACK.getBoolean()) {
215 when (event.actionMasked) { 212 when (event.actionMasked) {
216 MotionEvent.ACTION_DOWN, 213 MotionEvent.ACTION_DOWN,
217 MotionEvent.ACTION_POINTER_DOWN -> 214 MotionEvent.ACTION_POINTER_DOWN ->
@@ -255,10 +252,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
255 MotionEvent.ACTION_POINTER_DOWN -> 252 MotionEvent.ACTION_POINTER_DOWN ->
256 // If no button is being moved now, remember the currently touched button to move. 253 // If no button is being moved now, remember the currently touched button to move.
257 if (buttonBeingConfigured == null && 254 if (buttonBeingConfigured == null &&
258 button.bounds.contains( 255 button.bounds.contains(fingerPositionX, fingerPositionY)
259 fingerPositionX,
260 fingerPositionY
261 )
262 ) { 256 ) {
263 buttonBeingConfigured = button 257 buttonBeingConfigured = button
264 buttonBeingConfigured!!.onConfigureTouch(event) 258 buttonBeingConfigured!!.onConfigureTouch(event)
@@ -274,7 +268,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
274 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { 268 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
275 // Persist button position by saving new place. 269 // Persist button position by saving new place.
276 saveControlPosition( 270 saveControlPosition(
277 buttonBeingConfigured!!.prefId, 271 buttonBeingConfigured!!.overlayControlData.id,
278 buttonBeingConfigured!!.bounds.centerX(), 272 buttonBeingConfigured!!.bounds.centerX(),
279 buttonBeingConfigured!!.bounds.centerY(), 273 buttonBeingConfigured!!.bounds.centerY(),
280 layout 274 layout
@@ -307,7 +301,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
307 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { 301 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
308 // Persist button position by saving new place. 302 // Persist button position by saving new place.
309 saveControlPosition( 303 saveControlPosition(
310 Settings.PREF_BUTTON_DPAD, 304 OverlayControl.COMBINED_DPAD.id,
311 dpadBeingConfigured!!.bounds.centerX(), 305 dpadBeingConfigured!!.bounds.centerX(),
312 dpadBeingConfigured!!.bounds.centerY(), 306 dpadBeingConfigured!!.bounds.centerY(),
313 layout 307 layout
@@ -321,10 +315,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
321 when (event.action) { 315 when (event.action) {
322 MotionEvent.ACTION_DOWN, 316 MotionEvent.ACTION_DOWN,
323 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null && 317 MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
324 joystick.bounds.contains( 318 joystick.bounds.contains(fingerPositionX, fingerPositionY)
325 fingerPositionX,
326 fingerPositionY
327 )
328 ) { 319 ) {
329 joystickBeingConfigured = joystick 320 joystickBeingConfigured = joystick
330 joystickBeingConfigured!!.onConfigureTouch(event) 321 joystickBeingConfigured!!.onConfigureTouch(event)
@@ -351,231 +342,257 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
351 return true 342 return true
352 } 343 }
353 344
354 private fun addOverlayControls(layout: String) { 345 private fun addOverlayControls(layout: OverlayLayout) {
355 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) 346 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
356 if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) { 347 val overlayControlData = NativeConfig.getOverlayControlData()
357 overlayButtons.add( 348 for (data in overlayControlData) {
358 initializeOverlayButton( 349 if (!data.enabled) {
359 context, 350 continue
360 windowSize, 351 }
361 R.drawable.facebutton_a, 352
362 R.drawable.facebutton_a_depressed, 353 val position = data.positionFromLayout(layout)
363 ButtonType.BUTTON_A, 354 when (data.id) {
364 Settings.PREF_BUTTON_A, 355 OverlayControl.BUTTON_A.id -> {
365 layout 356 overlayButtons.add(
366 ) 357 initializeOverlayButton(
367 ) 358 context,
368 } 359 windowSize,
369 if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) { 360 R.drawable.facebutton_a,
370 overlayButtons.add( 361 R.drawable.facebutton_a_depressed,
371 initializeOverlayButton( 362 ButtonType.BUTTON_A,
372 context, 363 data,
373 windowSize, 364 position
374 R.drawable.facebutton_b, 365 )
375 R.drawable.facebutton_b_depressed, 366 )
376 ButtonType.BUTTON_B, 367 }
377 Settings.PREF_BUTTON_B, 368
378 layout 369 OverlayControl.BUTTON_B.id -> {
379 ) 370 overlayButtons.add(
380 ) 371 initializeOverlayButton(
381 } 372 context,
382 if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) { 373 windowSize,
383 overlayButtons.add( 374 R.drawable.facebutton_b,
384 initializeOverlayButton( 375 R.drawable.facebutton_b_depressed,
385 context, 376 ButtonType.BUTTON_B,
386 windowSize, 377 data,
387 R.drawable.facebutton_x, 378 position
388 R.drawable.facebutton_x_depressed, 379 )
389 ButtonType.BUTTON_X, 380 )
390 Settings.PREF_BUTTON_X, 381 }
391 layout 382
392 ) 383 OverlayControl.BUTTON_X.id -> {
393 ) 384 overlayButtons.add(
394 } 385 initializeOverlayButton(
395 if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) { 386 context,
396 overlayButtons.add( 387 windowSize,
397 initializeOverlayButton( 388 R.drawable.facebutton_x,
398 context, 389 R.drawable.facebutton_x_depressed,
399 windowSize, 390 ButtonType.BUTTON_X,
400 R.drawable.facebutton_y, 391 data,
401 R.drawable.facebutton_y_depressed, 392 position
402 ButtonType.BUTTON_Y, 393 )
403 Settings.PREF_BUTTON_Y, 394 )
404 layout 395 }
405 ) 396
406 ) 397 OverlayControl.BUTTON_Y.id -> {
407 } 398 overlayButtons.add(
408 if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) { 399 initializeOverlayButton(
409 overlayButtons.add( 400 context,
410 initializeOverlayButton( 401 windowSize,
411 context, 402 R.drawable.facebutton_y,
412 windowSize, 403 R.drawable.facebutton_y_depressed,
413 R.drawable.l_shoulder, 404 ButtonType.BUTTON_Y,
414 R.drawable.l_shoulder_depressed, 405 data,
415 ButtonType.TRIGGER_L, 406 position
416 Settings.PREF_BUTTON_L, 407 )
417 layout 408 )
418 ) 409 }
419 ) 410
420 } 411 OverlayControl.BUTTON_PLUS.id -> {
421 if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) { 412 overlayButtons.add(
422 overlayButtons.add( 413 initializeOverlayButton(
423 initializeOverlayButton( 414 context,
424 context, 415 windowSize,
425 windowSize, 416 R.drawable.facebutton_plus,
426 R.drawable.r_shoulder, 417 R.drawable.facebutton_plus_depressed,
427 R.drawable.r_shoulder_depressed, 418 ButtonType.BUTTON_PLUS,
428 ButtonType.TRIGGER_R, 419 data,
429 Settings.PREF_BUTTON_R, 420 position
430 layout 421 )
431 ) 422 )
432 ) 423 }
433 } 424
434 if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) { 425 OverlayControl.BUTTON_MINUS.id -> {
435 overlayButtons.add( 426 overlayButtons.add(
436 initializeOverlayButton( 427 initializeOverlayButton(
437 context, 428 context,
438 windowSize, 429 windowSize,
439 R.drawable.zl_trigger, 430 R.drawable.facebutton_minus,
440 R.drawable.zl_trigger_depressed, 431 R.drawable.facebutton_minus_depressed,
441 ButtonType.TRIGGER_ZL, 432 ButtonType.BUTTON_MINUS,
442 Settings.PREF_BUTTON_ZL, 433 data,
443 layout 434 position
444 ) 435 )
445 ) 436 )
446 } 437 }
447 if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) { 438
448 overlayButtons.add( 439 OverlayControl.BUTTON_HOME.id -> {
449 initializeOverlayButton( 440 overlayButtons.add(
450 context, 441 initializeOverlayButton(
451 windowSize, 442 context,
452 R.drawable.zr_trigger, 443 windowSize,
453 R.drawable.zr_trigger_depressed, 444 R.drawable.facebutton_home,
454 ButtonType.TRIGGER_ZR, 445 R.drawable.facebutton_home_depressed,
455 Settings.PREF_BUTTON_ZR, 446 ButtonType.BUTTON_HOME,
456 layout 447 data,
457 ) 448 position
458 ) 449 )
459 } 450 )
460 if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) { 451 }
461 overlayButtons.add( 452
462 initializeOverlayButton( 453 OverlayControl.BUTTON_CAPTURE.id -> {
463 context, 454 overlayButtons.add(
464 windowSize, 455 initializeOverlayButton(
465 R.drawable.facebutton_plus, 456 context,
466 R.drawable.facebutton_plus_depressed, 457 windowSize,
467 ButtonType.BUTTON_PLUS, 458 R.drawable.facebutton_screenshot,
468 Settings.PREF_BUTTON_PLUS, 459 R.drawable.facebutton_screenshot_depressed,
469 layout 460 ButtonType.BUTTON_CAPTURE,
470 ) 461 data,
471 ) 462 position
472 } 463 )
473 if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) { 464 )
474 overlayButtons.add( 465 }
475 initializeOverlayButton( 466
476 context, 467 OverlayControl.BUTTON_L.id -> {
477 windowSize, 468 overlayButtons.add(
478 R.drawable.facebutton_minus, 469 initializeOverlayButton(
479 R.drawable.facebutton_minus_depressed, 470 context,
480 ButtonType.BUTTON_MINUS, 471 windowSize,
481 Settings.PREF_BUTTON_MINUS, 472 R.drawable.l_shoulder,
482 layout 473 R.drawable.l_shoulder_depressed,
483 ) 474 ButtonType.TRIGGER_L,
484 ) 475 data,
485 } 476 position
486 if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) { 477 )
487 overlayDpads.add( 478 )
488 initializeOverlayDpad( 479 }
489 context, 480
490 windowSize, 481 OverlayControl.BUTTON_R.id -> {
491 R.drawable.dpad_standard, 482 overlayButtons.add(
492 R.drawable.dpad_standard_cardinal_depressed, 483 initializeOverlayButton(
493 R.drawable.dpad_standard_diagonal_depressed, 484 context,
494 layout 485 windowSize,
495 ) 486 R.drawable.r_shoulder,
496 ) 487 R.drawable.r_shoulder_depressed,
497 } 488 ButtonType.TRIGGER_R,
498 if (preferences.getBoolean(Settings.PREF_STICK_L, true)) { 489 data,
499 overlayJoysticks.add( 490 position
500 initializeOverlayJoystick( 491 )
501 context, 492 )
502 windowSize, 493 }
503 R.drawable.joystick_range, 494
504 R.drawable.joystick, 495 OverlayControl.BUTTON_ZL.id -> {
505 R.drawable.joystick_depressed, 496 overlayButtons.add(
506 StickType.STICK_L, 497 initializeOverlayButton(
507 ButtonType.STICK_L, 498 context,
508 Settings.PREF_STICK_L, 499 windowSize,
509 layout 500 R.drawable.zl_trigger,
510 ) 501 R.drawable.zl_trigger_depressed,
511 ) 502 ButtonType.TRIGGER_ZL,
512 } 503 data,
513 if (preferences.getBoolean(Settings.PREF_STICK_R, true)) { 504 position
514 overlayJoysticks.add( 505 )
515 initializeOverlayJoystick( 506 )
516 context, 507 }
517 windowSize, 508
518 R.drawable.joystick_range, 509 OverlayControl.BUTTON_ZR.id -> {
519 R.drawable.joystick, 510 overlayButtons.add(
520 R.drawable.joystick_depressed, 511 initializeOverlayButton(
521 StickType.STICK_R, 512 context,
522 ButtonType.STICK_R, 513 windowSize,
523 Settings.PREF_STICK_R, 514 R.drawable.zr_trigger,
524 layout 515 R.drawable.zr_trigger_depressed,
525 ) 516 ButtonType.TRIGGER_ZR,
526 ) 517 data,
527 } 518 position
528 if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) { 519 )
529 overlayButtons.add( 520 )
530 initializeOverlayButton( 521 }
531 context, 522
532 windowSize, 523 OverlayControl.BUTTON_STICK_L.id -> {
533 R.drawable.facebutton_home, 524 overlayButtons.add(
534 R.drawable.facebutton_home_depressed, 525 initializeOverlayButton(
535 ButtonType.BUTTON_HOME, 526 context,
536 Settings.PREF_BUTTON_HOME, 527 windowSize,
537 layout 528 R.drawable.button_l3,
538 ) 529 R.drawable.button_l3_depressed,
539 ) 530 ButtonType.STICK_L,
540 } 531 data,
541 if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) { 532 position
542 overlayButtons.add( 533 )
543 initializeOverlayButton( 534 )
544 context, 535 }
545 windowSize, 536
546 R.drawable.facebutton_screenshot, 537 OverlayControl.BUTTON_STICK_R.id -> {
547 R.drawable.facebutton_screenshot_depressed, 538 overlayButtons.add(
548 ButtonType.BUTTON_CAPTURE, 539 initializeOverlayButton(
549 Settings.PREF_BUTTON_SCREENSHOT, 540 context,
550 layout 541 windowSize,
551 ) 542 R.drawable.button_r3,
552 ) 543 R.drawable.button_r3_depressed,
553 } 544 ButtonType.STICK_R,
554 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) { 545 data,
555 overlayButtons.add( 546 position
556 initializeOverlayButton( 547 )
557 context, 548 )
558 windowSize, 549 }
559 R.drawable.button_l3, 550
560 R.drawable.button_l3_depressed, 551 OverlayControl.STICK_L.id -> {
561 ButtonType.STICK_L, 552 overlayJoysticks.add(
562 Settings.PREF_BUTTON_STICK_L, 553 initializeOverlayJoystick(
563 layout 554 context,
564 ) 555 windowSize,
565 ) 556 R.drawable.joystick_range,
566 } 557 R.drawable.joystick,
567 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) { 558 R.drawable.joystick_depressed,
568 overlayButtons.add( 559 StickType.STICK_L,
569 initializeOverlayButton( 560 ButtonType.STICK_L,
570 context, 561 data,
571 windowSize, 562 position
572 R.drawable.button_r3, 563 )
573 R.drawable.button_r3_depressed, 564 )
574 ButtonType.STICK_R, 565 }
575 Settings.PREF_BUTTON_STICK_R, 566
576 layout 567 OverlayControl.STICK_R.id -> {
577 ) 568 overlayJoysticks.add(
578 ) 569 initializeOverlayJoystick(
570 context,
571 windowSize,
572 R.drawable.joystick_range,
573 R.drawable.joystick,
574 R.drawable.joystick_depressed,
575 StickType.STICK_R,
576 ButtonType.STICK_R,
577 data,
578 position
579 )
580 )
581 }
582
583 OverlayControl.COMBINED_DPAD.id -> {
584 overlayDpads.add(
585 initializeOverlayDpad(
586 context,
587 windowSize,
588 R.drawable.dpad_standard,
589 R.drawable.dpad_standard_cardinal_depressed,
590 R.drawable.dpad_standard_diagonal_depressed,
591 position
592 )
593 )
594 }
595 }
579 } 596 }
580 } 597 }
581 598
@@ -586,313 +603,87 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
586 overlayJoysticks.clear() 603 overlayJoysticks.clear()
587 604
588 // Add all the enabled overlay items back to the HashSet. 605 // Add all the enabled overlay items back to the HashSet.
589 if (EmulationMenuSettings.showOverlay) { 606 if (BooleanSetting.SHOW_INPUT_OVERLAY.getBoolean()) {
590 addOverlayControls(layout) 607 addOverlayControls(layout)
591 } 608 }
592 invalidate() 609 invalidate()
593 } 610 }
594 611
595 private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) { 612 private fun saveControlPosition(id: String, x: Int, y: Int, layout: OverlayLayout) {
596 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight)) 613 val windowSize = getSafeScreenSize(context, Pair(measuredWidth, measuredHeight))
597 val min = windowSize.first 614 val min = windowSize.first
598 val max = windowSize.second 615 val max = windowSize.second
599 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 616 val overlayControlData = NativeConfig.getOverlayControlData()
600 .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x) 617 val data = overlayControlData.firstOrNull { it.id == id }
601 .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y) 618 val newPosition = Pair((x - min.x).toDouble() / max.x, (y - min.y).toDouble() / max.y)
602 .apply() 619 when (layout) {
620 OverlayLayout.Landscape -> data?.landscapePosition = newPosition
621 OverlayLayout.Portrait -> data?.portraitPosition = newPosition
622 OverlayLayout.Foldable -> data?.foldablePosition = newPosition
623 }
624 NativeConfig.setOverlayControlData(overlayControlData)
603 } 625 }
604 626
605 fun setIsInEditMode(editMode: Boolean) { 627 fun setIsInEditMode(editMode: Boolean) {
606 inEditMode = editMode 628 inEditMode = editMode
607 } 629 }
608 630
609 private fun resetCurrentLayout() { 631 /**
610 defaultOverlayByLayout(layout) 632 * Applies and saves all default values for the overlay
611 val layoutIndex = overlayLayouts.indexOf(layout) 633 */
612 preferences.edit() 634 private fun populateDefaultConfig() {
613 .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex]) 635 val newConfig = OverlayControl.entries.map { it.toOverlayControlData() }
614 .apply() 636 NativeConfig.setOverlayControlData(newConfig.toTypedArray())
637 NativeConfig.saveGlobalConfig()
615 } 638 }
616 639
617 private fun resetAllLayouts() { 640 /**
618 val editor = preferences.edit() 641 * Checks if any new controls were added to OverlayControl that do not exist within deserialized
619 overlayLayouts.forEachIndexed { i, layout -> 642 * config and adds / saves them if necessary
620 defaultOverlayByLayout(layout) 643 *
621 editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i]) 644 * @param overlayControlData Overlay control data from [NativeConfig.getOverlayControlData]
645 */
646 private fun checkForNewControls(overlayControlData: Array<OverlayControlData>) {
647 val missingControls = mutableListOf<OverlayControlData>()
648 OverlayControl.entries.forEach { defaultControl ->
649 val controlData = overlayControlData.firstOrNull { it.id == defaultControl.id }
650 if (controlData == null) {
651 missingControls.add(defaultControl.toOverlayControlData())
652 }
653 }
654
655 if (missingControls.isNotEmpty()) {
656 NativeConfig.setOverlayControlData(
657 arrayOf(*overlayControlData, *(missingControls.toTypedArray()))
658 )
659 NativeConfig.saveGlobalConfig()
622 } 660 }
623 editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
624 editor.apply()
625 } 661 }
626 662
627 fun resetLayoutVisibilityAndPlacement() { 663 fun resetLayoutVisibilityAndPlacement() {
628 defaultOverlayByLayout(layout) 664 defaultOverlayPositionByLayout(layout)
629 val editor = preferences.edit() 665
630 Settings.overlayPreferences.forEachIndexed { _, pref -> 666 val overlayControlData = NativeConfig.getOverlayControlData()
631 editor.remove(pref) 667 overlayControlData.forEach {
668 it.enabled = OverlayControl.from(it.id)?.defaultVisibility == false
632 } 669 }
633 editor.apply() 670 NativeConfig.setOverlayControlData(overlayControlData)
671
634 refreshControls() 672 refreshControls()
635 } 673 }
636 674
637 private val landscapeResources = arrayOf( 675 private fun defaultOverlayPositionByLayout(layout: OverlayLayout) {
638 R.integer.SWITCH_BUTTON_A_X, 676 val overlayControlData = NativeConfig.getOverlayControlData()
639 R.integer.SWITCH_BUTTON_A_Y, 677 for (data in overlayControlData) {
640 R.integer.SWITCH_BUTTON_B_X, 678 val defaultControlData = OverlayControl.from(data.id) ?: continue
641 R.integer.SWITCH_BUTTON_B_Y, 679 val position = defaultControlData.getDefaultPositionForLayout(layout)
642 R.integer.SWITCH_BUTTON_X_X, 680 when (layout) {
643 R.integer.SWITCH_BUTTON_X_Y, 681 OverlayLayout.Landscape -> data.landscapePosition = position
644 R.integer.SWITCH_BUTTON_Y_X, 682 OverlayLayout.Portrait -> data.portraitPosition = position
645 R.integer.SWITCH_BUTTON_Y_Y, 683 OverlayLayout.Foldable -> data.foldablePosition = position
646 R.integer.SWITCH_TRIGGER_ZL_X, 684 }
647 R.integer.SWITCH_TRIGGER_ZL_Y,
648 R.integer.SWITCH_TRIGGER_ZR_X,
649 R.integer.SWITCH_TRIGGER_ZR_Y,
650 R.integer.SWITCH_BUTTON_DPAD_X,
651 R.integer.SWITCH_BUTTON_DPAD_Y,
652 R.integer.SWITCH_TRIGGER_L_X,
653 R.integer.SWITCH_TRIGGER_L_Y,
654 R.integer.SWITCH_TRIGGER_R_X,
655 R.integer.SWITCH_TRIGGER_R_Y,
656 R.integer.SWITCH_BUTTON_PLUS_X,
657 R.integer.SWITCH_BUTTON_PLUS_Y,
658 R.integer.SWITCH_BUTTON_MINUS_X,
659 R.integer.SWITCH_BUTTON_MINUS_Y,
660 R.integer.SWITCH_BUTTON_HOME_X,
661 R.integer.SWITCH_BUTTON_HOME_Y,
662 R.integer.SWITCH_BUTTON_CAPTURE_X,
663 R.integer.SWITCH_BUTTON_CAPTURE_Y,
664 R.integer.SWITCH_STICK_R_X,
665 R.integer.SWITCH_STICK_R_Y,
666 R.integer.SWITCH_STICK_L_X,
667 R.integer.SWITCH_STICK_L_Y,
668 R.integer.SWITCH_BUTTON_STICK_L_X,
669 R.integer.SWITCH_BUTTON_STICK_L_Y,
670 R.integer.SWITCH_BUTTON_STICK_R_X,
671 R.integer.SWITCH_BUTTON_STICK_R_Y
672 )
673
674 private val portraitResources = arrayOf(
675 R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
676 R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
677 R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
678 R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
679 R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
680 R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
681 R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
682 R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
683 R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
684 R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
685 R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
686 R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
687 R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
688 R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
689 R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
690 R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
691 R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
692 R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
693 R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
694 R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
695 R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
696 R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
697 R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
698 R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
699 R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
700 R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
701 R.integer.SWITCH_STICK_R_X_PORTRAIT,
702 R.integer.SWITCH_STICK_R_Y_PORTRAIT,
703 R.integer.SWITCH_STICK_L_X_PORTRAIT,
704 R.integer.SWITCH_STICK_L_Y_PORTRAIT,
705 R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
706 R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
707 R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
708 R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
709 )
710
711 private val foldableResources = arrayOf(
712 R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
713 R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
714 R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
715 R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
716 R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
717 R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
718 R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
719 R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
720 R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
721 R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
722 R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
723 R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
724 R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
725 R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
726 R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
727 R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
728 R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
729 R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
730 R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
731 R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
732 R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
733 R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
734 R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
735 R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
736 R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
737 R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
738 R.integer.SWITCH_STICK_R_X_FOLDABLE,
739 R.integer.SWITCH_STICK_R_Y_FOLDABLE,
740 R.integer.SWITCH_STICK_L_X_FOLDABLE,
741 R.integer.SWITCH_STICK_L_Y_FOLDABLE,
742 R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
743 R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
744 R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
745 R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
746 )
747
748 private fun getResourceValue(layout: String, position: Int): Float {
749 return when (layout) {
750 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
751 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
752 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
753 } 685 }
754 } 686 NativeConfig.setOverlayControlData(overlayControlData)
755
756 private fun defaultOverlayByLayout(layout: String) {
757 // Each value represents the position of the button in relation to the screen size without insets.
758 preferences.edit()
759 .putFloat(
760 "${Settings.PREF_BUTTON_A}-X$layout",
761 getResourceValue(layout, 0)
762 )
763 .putFloat(
764 "${Settings.PREF_BUTTON_A}-Y$layout",
765 getResourceValue(layout, 1)
766 )
767 .putFloat(
768 "${Settings.PREF_BUTTON_B}-X$layout",
769 getResourceValue(layout, 2)
770 )
771 .putFloat(
772 "${Settings.PREF_BUTTON_B}-Y$layout",
773 getResourceValue(layout, 3)
774 )
775 .putFloat(
776 "${Settings.PREF_BUTTON_X}-X$layout",
777 getResourceValue(layout, 4)
778 )
779 .putFloat(
780 "${Settings.PREF_BUTTON_X}-Y$layout",
781 getResourceValue(layout, 5)
782 )
783 .putFloat(
784 "${Settings.PREF_BUTTON_Y}-X$layout",
785 getResourceValue(layout, 6)
786 )
787 .putFloat(
788 "${Settings.PREF_BUTTON_Y}-Y$layout",
789 getResourceValue(layout, 7)
790 )
791 .putFloat(
792 "${Settings.PREF_BUTTON_ZL}-X$layout",
793 getResourceValue(layout, 8)
794 )
795 .putFloat(
796 "${Settings.PREF_BUTTON_ZL}-Y$layout",
797 getResourceValue(layout, 9)
798 )
799 .putFloat(
800 "${Settings.PREF_BUTTON_ZR}-X$layout",
801 getResourceValue(layout, 10)
802 )
803 .putFloat(
804 "${Settings.PREF_BUTTON_ZR}-Y$layout",
805 getResourceValue(layout, 11)
806 )
807 .putFloat(
808 "${Settings.PREF_BUTTON_DPAD}-X$layout",
809 getResourceValue(layout, 12)
810 )
811 .putFloat(
812 "${Settings.PREF_BUTTON_DPAD}-Y$layout",
813 getResourceValue(layout, 13)
814 )
815 .putFloat(
816 "${Settings.PREF_BUTTON_L}-X$layout",
817 getResourceValue(layout, 14)
818 )
819 .putFloat(
820 "${Settings.PREF_BUTTON_L}-Y$layout",
821 getResourceValue(layout, 15)
822 )
823 .putFloat(
824 "${Settings.PREF_BUTTON_R}-X$layout",
825 getResourceValue(layout, 16)
826 )
827 .putFloat(
828 "${Settings.PREF_BUTTON_R}-Y$layout",
829 getResourceValue(layout, 17)
830 )
831 .putFloat(
832 "${Settings.PREF_BUTTON_PLUS}-X$layout",
833 getResourceValue(layout, 18)
834 )
835 .putFloat(
836 "${Settings.PREF_BUTTON_PLUS}-Y$layout",
837 getResourceValue(layout, 19)
838 )
839 .putFloat(
840 "${Settings.PREF_BUTTON_MINUS}-X$layout",
841 getResourceValue(layout, 20)
842 )
843 .putFloat(
844 "${Settings.PREF_BUTTON_MINUS}-Y$layout",
845 getResourceValue(layout, 21)
846 )
847 .putFloat(
848 "${Settings.PREF_BUTTON_HOME}-X$layout",
849 getResourceValue(layout, 22)
850 )
851 .putFloat(
852 "${Settings.PREF_BUTTON_HOME}-Y$layout",
853 getResourceValue(layout, 23)
854 )
855 .putFloat(
856 "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
857 getResourceValue(layout, 24)
858 )
859 .putFloat(
860 "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
861 getResourceValue(layout, 25)
862 )
863 .putFloat(
864 "${Settings.PREF_STICK_R}-X$layout",
865 getResourceValue(layout, 26)
866 )
867 .putFloat(
868 "${Settings.PREF_STICK_R}-Y$layout",
869 getResourceValue(layout, 27)
870 )
871 .putFloat(
872 "${Settings.PREF_STICK_L}-X$layout",
873 getResourceValue(layout, 28)
874 )
875 .putFloat(
876 "${Settings.PREF_STICK_L}-Y$layout",
877 getResourceValue(layout, 29)
878 )
879 .putFloat(
880 "${Settings.PREF_BUTTON_STICK_L}-X$layout",
881 getResourceValue(layout, 30)
882 )
883 .putFloat(
884 "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
885 getResourceValue(layout, 31)
886 )
887 .putFloat(
888 "${Settings.PREF_BUTTON_STICK_R}-X$layout",
889 getResourceValue(layout, 32)
890 )
891 .putFloat(
892 "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
893 getResourceValue(layout, 33)
894 )
895 .apply()
896 } 687 }
897 688
898 override fun isInEditMode(): Boolean { 689 override fun isInEditMode(): Boolean {
@@ -913,18 +704,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
913 FOLDABLE_OVERLAY_VERSION 704 FOLDABLE_OVERLAY_VERSION
914 ) 705 )
915 706
916 private val preferences: SharedPreferences =
917 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
918
919 const val LANDSCAPE = "_Landscape"
920 const val PORTRAIT = "_Portrait"
921 const val FOLDABLE = "_Foldable"
922 val overlayLayouts = listOf(
923 LANDSCAPE,
924 PORTRAIT,
925 FOLDABLE
926 )
927
928 /** 707 /**
929 * Resizes a [Bitmap] by a given scale factor 708 * Resizes a [Bitmap] by a given scale factor
930 * 709 *
@@ -1036,29 +815,19 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1036 * In the input overlay configuration menu, 815 * In the input overlay configuration menu,
1037 * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). 816 * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay).
1038 * the X and Y coordinates of the button at the END of its touch event 817 * the X and Y coordinates of the button at the END of its touch event
1039 * (when you remove your finger/stylus from the touchscreen) are then stored 818 * (when you remove your finger/stylus from the touchscreen) are then stored in a native .
1040 * within a SharedPreferences instance so that those values can be retrieved here.
1041 *
1042 *
1043 * This has a few benefits over the conventional way of storing the values
1044 * (ie. within the yuzu ini file).
1045 *
1046 * * No native calls
1047 * * Keeps Android-only values inside the Android environment
1048 *
1049 *
1050 * 819 *
1051 * Technically no modifications should need to be performed on the returned 820 * Technically no modifications should need to be performed on the returned
1052 * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait 821 * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait
1053 * for Android to call the onDraw method. 822 * for Android to call the onDraw method.
1054 * 823 *
1055 * @param context The current [Context]. 824 * @param context The current [Context].
1056 * @param windowSize The size of the window to draw the overlay on. 825 * @param windowSize The size of the window to draw the overlay on.
1057 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). 826 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
1058 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). 827 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
1059 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. 828 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
1060 * @param prefId Identifier for determining where a button appears on screen. 829 * @param overlayControlData Identifier for determining where a button appears on screen.
1061 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 830 * @param position The position on screen as represented by an x and y value between 0 and 1.
1062 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. 831 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
1063 */ 832 */
1064 private fun initializeOverlayButton( 833 private fun initializeOverlayButton(
@@ -1067,33 +836,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1067 defaultResId: Int, 836 defaultResId: Int,
1068 pressedResId: Int, 837 pressedResId: Int,
1069 buttonId: Int, 838 buttonId: Int,
1070 prefId: String, 839 overlayControlData: OverlayControlData,
1071 layout: String 840 position: Pair<Double, Double>
1072 ): InputOverlayDrawableButton { 841 ): InputOverlayDrawableButton {
1073 // Resources handle for fetching the initial Drawable resource. 842 // Resources handle for fetching the initial Drawable resource.
1074 val res = context.resources 843 val res = context.resources
1075 844
1076 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
1077 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1078
1079 // Decide scale based on button preference ID and user preference 845 // Decide scale based on button preference ID and user preference
1080 var scale: Float = when (prefId) { 846 var scale: Float = when (overlayControlData.id) {
1081 Settings.PREF_BUTTON_HOME, 847 OverlayControl.BUTTON_HOME.id,
1082 Settings.PREF_BUTTON_SCREENSHOT, 848 OverlayControl.BUTTON_CAPTURE.id,
1083 Settings.PREF_BUTTON_PLUS, 849 OverlayControl.BUTTON_PLUS.id,
1084 Settings.PREF_BUTTON_MINUS -> 0.07f 850 OverlayControl.BUTTON_MINUS.id -> 0.07f
1085 851
1086 Settings.PREF_BUTTON_L, 852 OverlayControl.BUTTON_L.id,
1087 Settings.PREF_BUTTON_R, 853 OverlayControl.BUTTON_R.id,
1088 Settings.PREF_BUTTON_ZL, 854 OverlayControl.BUTTON_ZL.id,
1089 Settings.PREF_BUTTON_ZR -> 0.26f 855 OverlayControl.BUTTON_ZR.id -> 0.26f
1090 856
1091 Settings.PREF_BUTTON_STICK_L, 857 OverlayControl.BUTTON_STICK_L.id,
1092 Settings.PREF_BUTTON_STICK_R -> 0.155f 858 OverlayControl.BUTTON_STICK_R.id -> 0.155f
1093 859
1094 else -> 0.11f 860 else -> 0.11f
1095 } 861 }
1096 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 862 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1097 scale /= 100f 863 scale /= 100f
1098 864
1099 // Initialize the InputOverlayDrawableButton. 865 // Initialize the InputOverlayDrawableButton.
@@ -1104,7 +870,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1104 defaultStateBitmap, 870 defaultStateBitmap,
1105 pressedStateBitmap, 871 pressedStateBitmap,
1106 buttonId, 872 buttonId,
1107 prefId 873 overlayControlData
1108 ) 874 )
1109 875
1110 // Get the minimum and maximum coordinates of the screen where the button can be placed. 876 // Get the minimum and maximum coordinates of the screen where the button can be placed.
@@ -1113,12 +879,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1113 879
1114 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 880 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1115 // These were set in the input overlay configuration menu. 881 // These were set in the input overlay configuration menu.
1116 val xKey = "$prefId-X$layout" 882 val drawableX = (position.first * max.x + min.x).toInt()
1117 val yKey = "$prefId-Y$layout" 883 val drawableY = (position.second * max.y + min.y).toInt()
1118 val drawableXPercent = sPrefs.getFloat(xKey, 0f)
1119 val drawableYPercent = sPrefs.getFloat(yKey, 0f)
1120 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1121 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1122 val width = overlayDrawable.width 884 val width = overlayDrawable.width
1123 val height = overlayDrawable.height 885 val height = overlayDrawable.height
1124 886
@@ -1136,8 +898,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1136 drawableX - (width / 2), 898 drawableX - (width / 2),
1137 drawableY - (height / 2) 899 drawableY - (height / 2)
1138 ) 900 )
1139 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 901 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1140 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1141 return overlayDrawable 902 return overlayDrawable
1142 } 903 }
1143 904
@@ -1149,7 +910,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1149 * @param defaultResId The [Bitmap] resource ID of the default state. 910 * @param defaultResId The [Bitmap] resource ID of the default state.
1150 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. 911 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
1151 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. 912 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
1152 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 913 * @param position The position on screen as represented by an x and y value between 0 and 1.
1153 * @return The initialized [InputOverlayDrawableDpad] 914 * @return The initialized [InputOverlayDrawableDpad]
1154 */ 915 */
1155 private fun initializeOverlayDpad( 916 private fun initializeOverlayDpad(
@@ -1158,17 +919,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1158 defaultResId: Int, 919 defaultResId: Int,
1159 pressedOneDirectionResId: Int, 920 pressedOneDirectionResId: Int,
1160 pressedTwoDirectionsResId: Int, 921 pressedTwoDirectionsResId: Int,
1161 layout: String 922 position: Pair<Double, Double>
1162 ): InputOverlayDrawableDpad { 923 ): InputOverlayDrawableDpad {
1163 // Resources handle for fetching the initial Drawable resource. 924 // Resources handle for fetching the initial Drawable resource.
1164 val res = context.resources 925 val res = context.resources
1165 926
1166 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad.
1167 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1168
1169 // Decide scale based on button ID and user preference 927 // Decide scale based on button ID and user preference
1170 var scale = 0.25f 928 var scale = 0.25f
1171 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 929 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1172 scale /= 100f 930 scale /= 100f
1173 931
1174 // Initialize the InputOverlayDrawableDpad. 932 // Initialize the InputOverlayDrawableDpad.
@@ -1195,10 +953,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1195 953
1196 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. 954 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
1197 // These were set in the input overlay configuration menu. 955 // These were set in the input overlay configuration menu.
1198 val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f) 956 val drawableX = (position.first * max.x + min.x).toInt()
1199 val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f) 957 val drawableY = (position.second * max.y + min.y).toInt()
1200 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1201 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1202 val width = overlayDrawable.width 958 val width = overlayDrawable.width
1203 val height = overlayDrawable.height 959 val height = overlayDrawable.height
1204 960
@@ -1213,8 +969,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1213 969
1214 // Need to set the image's position 970 // Need to set the image's position
1215 overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2)) 971 overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2))
1216 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 972 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1217 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1218 return overlayDrawable 973 return overlayDrawable
1219 } 974 }
1220 975
@@ -1227,9 +982,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1227 * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). 982 * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around).
1228 * @param pressedResInner Resource ID for the pressed inner image of the joystick. 983 * @param pressedResInner Resource ID for the pressed inner image of the joystick.
1229 * @param joystick Identifier for which joystick this is. 984 * @param joystick Identifier for which joystick this is.
1230 * @param button Identifier for which joystick button this is. 985 * @param buttonId Identifier for which joystick button this is.
1231 * @param prefId Identifier for determining where a button appears on screen. 986 * @param overlayControlData Identifier for determining where a button appears on screen.
1232 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. 987 * @param position The position on screen as represented by an x and y value between 0 and 1.
1233 * @return The initialized [InputOverlayDrawableJoystick]. 988 * @return The initialized [InputOverlayDrawableJoystick].
1234 */ 989 */
1235 private fun initializeOverlayJoystick( 990 private fun initializeOverlayJoystick(
@@ -1239,19 +994,16 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1239 defaultResInner: Int, 994 defaultResInner: Int,
1240 pressedResInner: Int, 995 pressedResInner: Int,
1241 joystick: Int, 996 joystick: Int,
1242 button: Int, 997 buttonId: Int,
1243 prefId: String, 998 overlayControlData: OverlayControlData,
1244 layout: String 999 position: Pair<Double, Double>
1245 ): InputOverlayDrawableJoystick { 1000 ): InputOverlayDrawableJoystick {
1246 // Resources handle for fetching the initial Drawable resource. 1001 // Resources handle for fetching the initial Drawable resource.
1247 val res = context.resources 1002 val res = context.resources
1248 1003
1249 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick.
1250 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
1251
1252 // Decide scale based on user preference 1004 // Decide scale based on user preference
1253 var scale = 0.3f 1005 var scale = 0.3f
1254 scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() 1006 scale *= (IntSetting.OVERLAY_SCALE.getInt() + 50).toFloat()
1255 scale /= 100f 1007 scale /= 100f
1256 1008
1257 // Initialize the InputOverlayDrawableJoystick. 1009 // Initialize the InputOverlayDrawableJoystick.
@@ -1265,10 +1017,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1265 1017
1266 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1018 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1267 // These were set in the input overlay configuration menu. 1019 // These were set in the input overlay configuration menu.
1268 val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f) 1020 val drawableX = (position.first * max.x + min.x).toInt()
1269 val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f) 1021 val drawableY = (position.second * max.y + min.y).toInt()
1270 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1271 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1272 val outerScale = 1.66f 1022 val outerScale = 1.66f
1273 1023
1274 // Now set the bounds for the InputOverlayDrawableJoystick. 1024 // Now set the bounds for the InputOverlayDrawableJoystick.
@@ -1292,14 +1042,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1292 outerRect, 1042 outerRect,
1293 innerRect, 1043 innerRect,
1294 joystick, 1044 joystick,
1295 button, 1045 buttonId,
1296 prefId 1046 overlayControlData.id
1297 ) 1047 )
1298 1048
1299 // Need to set the image's position 1049 // Need to set the image's position
1300 overlayDrawable.setPosition(drawableX, drawableY) 1050 overlayDrawable.setPosition(drawableX, drawableY)
1301 val savedOpacity = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100) 1051 overlayDrawable.setOpacity(IntSetting.OVERLAY_OPACITY.getInt() * 255 / 100)
1302 overlayDrawable.setOpacity(savedOpacity * 255 / 100)
1303 return overlayDrawable 1052 return overlayDrawable
1304 } 1053 }
1305 } 1054 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
index 2c28dda88..b14a4f96e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt
@@ -10,6 +10,7 @@ import android.graphics.Rect
10import android.graphics.drawable.BitmapDrawable 10import android.graphics.drawable.BitmapDrawable
11import android.view.MotionEvent 11import android.view.MotionEvent
12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState 12import org.yuzu.yuzu_emu.NativeLibrary.ButtonState
13import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
13 14
14/** 15/**
15 * Custom [BitmapDrawable] that is capable 16 * Custom [BitmapDrawable] that is capable
@@ -25,7 +26,7 @@ class InputOverlayDrawableButton(
25 defaultStateBitmap: Bitmap, 26 defaultStateBitmap: Bitmap,
26 pressedStateBitmap: Bitmap, 27 pressedStateBitmap: Bitmap,
27 val buttonId: Int, 28 val buttonId: Int,
28 val prefId: String 29 val overlayControlData: OverlayControlData
29) { 30) {
30 // The ID value what motion event is tracking 31 // The ID value what motion event is tracking
31 var trackId: Int 32 var trackId: Int
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
index 518b1e783..113bf7c24 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -14,7 +14,7 @@ import kotlin.math.cos
14import kotlin.math.sin 14import kotlin.math.sin
15import kotlin.math.sqrt 15import kotlin.math.sqrt
16import org.yuzu.yuzu_emu.NativeLibrary 16import org.yuzu.yuzu_emu.NativeLibrary
17import org.yuzu.yuzu_emu.utils.EmulationMenuSettings 17import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
18 18
19/** 19/**
20 * Custom [BitmapDrawable] that is capable 20 * Custom [BitmapDrawable] that is capable
@@ -125,7 +125,7 @@ class InputOverlayDrawableJoystick(
125 pressedState = true 125 pressedState = true
126 outerBitmap.alpha = 0 126 outerBitmap.alpha = 0
127 boundsBoxBitmap.alpha = opacity 127 boundsBoxBitmap.alpha = opacity
128 if (EmulationMenuSettings.joystickRelCenter) { 128 if (BooleanSetting.JOYSTICK_REL_CENTER.getBoolean()) {
129 virtBounds.offset( 129 virtBounds.offset(
130 xPosition - virtBounds.centerX(), 130 xPosition - virtBounds.centerX(),
131 yPosition - virtBounds.centerY() 131 yPosition - virtBounds.centerY()
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
new file mode 100644
index 000000000..a0eeadf4b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControl.kt
@@ -0,0 +1,188 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6import androidx.annotation.IntegerRes
7import org.yuzu.yuzu_emu.R
8import org.yuzu.yuzu_emu.YuzuApplication
9
10enum class OverlayControl(
11 val id: String,
12 val defaultVisibility: Boolean,
13 @IntegerRes val defaultLandscapePositionResources: Pair<Int, Int>,
14 @IntegerRes val defaultPortraitPositionResources: Pair<Int, Int>,
15 @IntegerRes val defaultFoldablePositionResources: Pair<Int, Int>
16) {
17 BUTTON_A(
18 "button_a",
19 true,
20 Pair(R.integer.BUTTON_A_X, R.integer.BUTTON_A_Y),
21 Pair(R.integer.BUTTON_A_X_PORTRAIT, R.integer.BUTTON_A_Y_PORTRAIT),
22 Pair(R.integer.BUTTON_A_X_FOLDABLE, R.integer.BUTTON_A_Y_FOLDABLE)
23 ),
24 BUTTON_B(
25 "button_b",
26 true,
27 Pair(R.integer.BUTTON_B_X, R.integer.BUTTON_B_Y),
28 Pair(R.integer.BUTTON_B_X_PORTRAIT, R.integer.BUTTON_B_Y_PORTRAIT),
29 Pair(R.integer.BUTTON_B_X_FOLDABLE, R.integer.BUTTON_B_Y_FOLDABLE)
30 ),
31 BUTTON_X(
32 "button_x",
33 true,
34 Pair(R.integer.BUTTON_X_X, R.integer.BUTTON_X_Y),
35 Pair(R.integer.BUTTON_X_X_PORTRAIT, R.integer.BUTTON_X_Y_PORTRAIT),
36 Pair(R.integer.BUTTON_X_X_FOLDABLE, R.integer.BUTTON_X_Y_FOLDABLE)
37 ),
38 BUTTON_Y(
39 "button_y",
40 true,
41 Pair(R.integer.BUTTON_Y_X, R.integer.BUTTON_Y_Y),
42 Pair(R.integer.BUTTON_Y_X_PORTRAIT, R.integer.BUTTON_Y_Y_PORTRAIT),
43 Pair(R.integer.BUTTON_Y_X_FOLDABLE, R.integer.BUTTON_Y_Y_FOLDABLE)
44 ),
45 BUTTON_PLUS(
46 "button_plus",
47 true,
48 Pair(R.integer.BUTTON_PLUS_X, R.integer.BUTTON_PLUS_Y),
49 Pair(R.integer.BUTTON_PLUS_X_PORTRAIT, R.integer.BUTTON_PLUS_Y_PORTRAIT),
50 Pair(R.integer.BUTTON_PLUS_X_FOLDABLE, R.integer.BUTTON_PLUS_Y_FOLDABLE)
51 ),
52 BUTTON_MINUS(
53 "button_minus",
54 true,
55 Pair(R.integer.BUTTON_MINUS_X, R.integer.BUTTON_MINUS_Y),
56 Pair(R.integer.BUTTON_MINUS_X_PORTRAIT, R.integer.BUTTON_MINUS_Y_PORTRAIT),
57 Pair(R.integer.BUTTON_MINUS_X_FOLDABLE, R.integer.BUTTON_MINUS_Y_FOLDABLE)
58 ),
59 BUTTON_HOME(
60 "button_home",
61 false,
62 Pair(R.integer.BUTTON_HOME_X, R.integer.BUTTON_HOME_Y),
63 Pair(R.integer.BUTTON_HOME_X_PORTRAIT, R.integer.BUTTON_HOME_Y_PORTRAIT),
64 Pair(R.integer.BUTTON_HOME_X_FOLDABLE, R.integer.BUTTON_HOME_Y_FOLDABLE)
65 ),
66 BUTTON_CAPTURE(
67 "button_capture",
68 false,
69 Pair(R.integer.BUTTON_CAPTURE_X, R.integer.BUTTON_CAPTURE_Y),
70 Pair(R.integer.BUTTON_CAPTURE_X_PORTRAIT, R.integer.BUTTON_CAPTURE_Y_PORTRAIT),
71 Pair(R.integer.BUTTON_CAPTURE_X_FOLDABLE, R.integer.BUTTON_CAPTURE_Y_FOLDABLE)
72 ),
73 BUTTON_L(
74 "button_l",
75 true,
76 Pair(R.integer.BUTTON_L_X, R.integer.BUTTON_L_Y),
77 Pair(R.integer.BUTTON_L_X_PORTRAIT, R.integer.BUTTON_L_Y_PORTRAIT),
78 Pair(R.integer.BUTTON_L_X_FOLDABLE, R.integer.BUTTON_L_Y_FOLDABLE)
79 ),
80 BUTTON_R(
81 "button_r",
82 true,
83 Pair(R.integer.BUTTON_R_X, R.integer.BUTTON_R_Y),
84 Pair(R.integer.BUTTON_R_X_PORTRAIT, R.integer.BUTTON_R_Y_PORTRAIT),
85 Pair(R.integer.BUTTON_R_X_FOLDABLE, R.integer.BUTTON_R_Y_FOLDABLE)
86 ),
87 BUTTON_ZL(
88 "button_zl",
89 true,
90 Pair(R.integer.BUTTON_ZL_X, R.integer.BUTTON_ZL_Y),
91 Pair(R.integer.BUTTON_ZL_X_PORTRAIT, R.integer.BUTTON_ZL_Y_PORTRAIT),
92 Pair(R.integer.BUTTON_ZL_X_FOLDABLE, R.integer.BUTTON_ZL_Y_FOLDABLE)
93 ),
94 BUTTON_ZR(
95 "button_zr",
96 true,
97 Pair(R.integer.BUTTON_ZR_X, R.integer.BUTTON_ZR_Y),
98 Pair(R.integer.BUTTON_ZR_X_PORTRAIT, R.integer.BUTTON_ZR_Y_PORTRAIT),
99 Pair(R.integer.BUTTON_ZR_X_FOLDABLE, R.integer.BUTTON_ZR_Y_FOLDABLE)
100 ),
101 BUTTON_STICK_L(
102 "button_stick_l",
103 true,
104 Pair(R.integer.BUTTON_STICK_L_X, R.integer.BUTTON_STICK_L_Y),
105 Pair(R.integer.BUTTON_STICK_L_X_PORTRAIT, R.integer.BUTTON_STICK_L_Y_PORTRAIT),
106 Pair(R.integer.BUTTON_STICK_L_X_FOLDABLE, R.integer.BUTTON_STICK_L_Y_FOLDABLE)
107 ),
108 BUTTON_STICK_R(
109 "button_stick_r",
110 true,
111 Pair(R.integer.BUTTON_STICK_R_X, R.integer.BUTTON_STICK_R_Y),
112 Pair(R.integer.BUTTON_STICK_R_X_PORTRAIT, R.integer.BUTTON_STICK_R_Y_PORTRAIT),
113 Pair(R.integer.BUTTON_STICK_R_X_FOLDABLE, R.integer.BUTTON_STICK_R_Y_FOLDABLE)
114 ),
115 STICK_L(
116 "stick_l",
117 true,
118 Pair(R.integer.STICK_L_X, R.integer.STICK_L_Y),
119 Pair(R.integer.STICK_L_X_PORTRAIT, R.integer.STICK_L_Y_PORTRAIT),
120 Pair(R.integer.STICK_L_X_FOLDABLE, R.integer.STICK_L_Y_FOLDABLE)
121 ),
122 STICK_R(
123 "stick_r",
124 true,
125 Pair(R.integer.STICK_R_X, R.integer.STICK_R_Y),
126 Pair(R.integer.STICK_R_X_PORTRAIT, R.integer.STICK_R_Y_PORTRAIT),
127 Pair(R.integer.STICK_R_X_FOLDABLE, R.integer.STICK_R_Y_FOLDABLE)
128 ),
129 COMBINED_DPAD(
130 "combined_dpad",
131 true,
132 Pair(R.integer.COMBINED_DPAD_X, R.integer.COMBINED_DPAD_Y),
133 Pair(R.integer.COMBINED_DPAD_X_PORTRAIT, R.integer.COMBINED_DPAD_Y_PORTRAIT),
134 Pair(R.integer.COMBINED_DPAD_X_FOLDABLE, R.integer.COMBINED_DPAD_Y_FOLDABLE)
135 );
136
137 fun getDefaultPositionForLayout(layout: OverlayLayout): Pair<Double, Double> {
138 val rawResourcePair: Pair<Int, Int>
139 YuzuApplication.appContext.resources.apply {
140 rawResourcePair = when (layout) {
141 OverlayLayout.Landscape -> {
142 Pair(
143 getInteger(this@OverlayControl.defaultLandscapePositionResources.first),
144 getInteger(this@OverlayControl.defaultLandscapePositionResources.second)
145 )
146 }
147
148 OverlayLayout.Portrait -> {
149 Pair(
150 getInteger(this@OverlayControl.defaultPortraitPositionResources.first),
151 getInteger(this@OverlayControl.defaultPortraitPositionResources.second)
152 )
153 }
154
155 OverlayLayout.Foldable -> {
156 Pair(
157 getInteger(this@OverlayControl.defaultFoldablePositionResources.first),
158 getInteger(this@OverlayControl.defaultFoldablePositionResources.second)
159 )
160 }
161 }
162 }
163
164 return Pair(
165 rawResourcePair.first.toDouble() / 1000,
166 rawResourcePair.second.toDouble() / 1000
167 )
168 }
169
170 fun toOverlayControlData(): OverlayControlData =
171 OverlayControlData(
172 id,
173 defaultVisibility,
174 getDefaultPositionForLayout(OverlayLayout.Landscape),
175 getDefaultPositionForLayout(OverlayLayout.Portrait),
176 getDefaultPositionForLayout(OverlayLayout.Foldable)
177 )
178
179 companion object {
180 val map: HashMap<String, OverlayControl> by lazy {
181 val hashMap = hashMapOf<String, OverlayControl>()
182 entries.forEach { hashMap[it.id] = it }
183 hashMap
184 }
185
186 fun from(id: String): OverlayControl? = map[id]
187 }
188}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
new file mode 100644
index 000000000..26cfeb1db
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlData.kt
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6data class OverlayControlData(
7 val id: String,
8 var enabled: Boolean,
9 var landscapePosition: Pair<Double, Double>,
10 var portraitPosition: Pair<Double, Double>,
11 var foldablePosition: Pair<Double, Double>
12) {
13 fun positionFromLayout(layout: OverlayLayout): Pair<Double, Double> =
14 when (layout) {
15 OverlayLayout.Landscape -> landscapePosition
16 OverlayLayout.Portrait -> portraitPosition
17 OverlayLayout.Foldable -> foldablePosition
18 }
19}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
new file mode 100644
index 000000000..6bd74c82f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayControlDefault.kt
@@ -0,0 +1,13 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6import androidx.annotation.IntegerRes
7
8data class OverlayControlDefault(
9 val buttonId: String,
10 @IntegerRes val landscapePositionResource: Pair<Int, Int>,
11 @IntegerRes val portraitPositionResource: Pair<Int, Int>,
12 @IntegerRes val foldablePositionResource: Pair<Int, Int>
13)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
new file mode 100644
index 000000000..d728164e5
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/model/OverlayLayout.kt
@@ -0,0 +1,10 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.overlay.model
5
6enum class OverlayLayout(val id: String) {
7 Landscape("Landscape"),
8 Portrait("Portrait"),
9 Foldable("Foldable")
10}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index fc0eeb9ad..54380323e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -91,18 +91,20 @@ class GamesFragment : Fragment() {
91 viewLifecycleOwner.lifecycleScope.apply { 91 viewLifecycleOwner.lifecycleScope.apply {
92 launch { 92 launch {
93 repeatOnLifecycle(Lifecycle.State.RESUMED) { 93 repeatOnLifecycle(Lifecycle.State.RESUMED) {
94 gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it } 94 gamesViewModel.isReloading.collect {
95 binding.swipeRefresh.isRefreshing = it
96 if (gamesViewModel.games.value.isEmpty() && !it) {
97 binding.noticeText.visibility = View.VISIBLE
98 } else {
99 binding.noticeText.visibility = View.INVISIBLE
100 }
101 }
95 } 102 }
96 } 103 }
97 launch { 104 launch {
98 repeatOnLifecycle(Lifecycle.State.RESUMED) { 105 repeatOnLifecycle(Lifecycle.State.RESUMED) {
99 gamesViewModel.games.collectLatest { 106 gamesViewModel.games.collectLatest {
100 (binding.gridGames.adapter as GameAdapter).submitList(it) 107 (binding.gridGames.adapter as GameAdapter).submitList(it)
101 if (it.isEmpty()) {
102 binding.noticeText.visibility = View.VISIBLE
103 } else {
104 binding.noticeText.visibility = View.GONE
105 }
106 } 108 }
107 } 109 }
108 } 110 }
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 b4117d761..622ae996e 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
@@ -625,7 +625,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
625 File(DirectoryInitialization.userDirectory!!), 625 File(DirectoryInitialization.userDirectory!!),
626 DirectoryInitialization.userDirectory!!, 626 DirectoryInitialization.userDirectory!!,
627 BufferedOutputStream(contentResolver.openOutputStream(result)), 627 BufferedOutputStream(contentResolver.openOutputStream(result)),
628 taskViewModel.cancelled 628 taskViewModel.cancelled,
629 compression = false
629 ) 630 )
630 return@newInstance when (zipResult) { 631 return@newInstance when (zipResult) {
631 TaskState.Completed -> getString(R.string.user_data_export_success) 632 TaskState.Completed -> getString(R.string.user_data_export_success)
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 0197fd712..de0794a17 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
@@ -3,9 +3,17 @@
3 3
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import androidx.preference.PreferenceManager
6import java.io.IOException 7import java.io.IOException
7import org.yuzu.yuzu_emu.NativeLibrary 8import org.yuzu.yuzu_emu.NativeLibrary
8import org.yuzu.yuzu_emu.YuzuApplication 9import org.yuzu.yuzu_emu.YuzuApplication
10import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
11import org.yuzu.yuzu_emu.features.settings.model.IntSetting
12import org.yuzu.yuzu_emu.features.settings.model.Settings
13import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
14import org.yuzu.yuzu_emu.overlay.model.OverlayControl
15import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
16import org.yuzu.yuzu_emu.utils.PreferenceUtil.migratePreference
9 17
10object DirectoryInitialization { 18object DirectoryInitialization {
11 private var userPath: String? = null 19 private var userPath: String? = null
@@ -17,6 +25,7 @@ object DirectoryInitialization {
17 initializeInternalStorage() 25 initializeInternalStorage()
18 NativeLibrary.initializeSystem(false) 26 NativeLibrary.initializeSystem(false)
19 NativeConfig.initializeGlobalConfig() 27 NativeConfig.initializeGlobalConfig()
28 migrateSettings()
20 areDirectoriesReady = true 29 areDirectoriesReady = true
21 } 30 }
22 } 31 }
@@ -35,4 +44,170 @@ object DirectoryInitialization {
35 e.printStackTrace() 44 e.printStackTrace()
36 } 45 }
37 } 46 }
47
48 private fun migrateSettings() {
49 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
50 var saveConfig = false
51 val theme = preferences.migratePreference<Int>(Settings.PREF_THEME)
52 if (theme != null) {
53 IntSetting.THEME.setInt(theme)
54 saveConfig = true
55 }
56
57 val themeMode = preferences.migratePreference<Int>(Settings.PREF_THEME_MODE)
58 if (themeMode != null) {
59 IntSetting.THEME_MODE.setInt(themeMode)
60 saveConfig = true
61 }
62
63 val blackBackgrounds =
64 preferences.migratePreference<Boolean>(Settings.PREF_BLACK_BACKGROUNDS)
65 if (blackBackgrounds != null) {
66 BooleanSetting.BLACK_BACKGROUNDS.setBoolean(blackBackgrounds)
67 saveConfig = true
68 }
69
70 val joystickRelCenter =
71 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER)
72 if (joystickRelCenter != null) {
73 BooleanSetting.JOYSTICK_REL_CENTER.setBoolean(joystickRelCenter)
74 saveConfig = true
75 }
76
77 val dpadSlide =
78 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE)
79 if (dpadSlide != null) {
80 BooleanSetting.DPAD_SLIDE.setBoolean(dpadSlide)
81 saveConfig = true
82 }
83
84 val hapticFeedback =
85 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_HAPTICS)
86 if (hapticFeedback != null) {
87 BooleanSetting.HAPTIC_FEEDBACK.setBoolean(hapticFeedback)
88 saveConfig = true
89 }
90
91 val showPerformanceOverlay =
92 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_FPS)
93 if (showPerformanceOverlay != null) {
94 BooleanSetting.SHOW_PERFORMANCE_OVERLAY.setBoolean(showPerformanceOverlay)
95 saveConfig = true
96 }
97
98 val showInputOverlay =
99 preferences.migratePreference<Boolean>(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY)
100 if (showInputOverlay != null) {
101 BooleanSetting.SHOW_INPUT_OVERLAY.setBoolean(showInputOverlay)
102 saveConfig = true
103 }
104
105 val overlayOpacity = preferences.migratePreference<Int>(Settings.PREF_CONTROL_OPACITY)
106 if (overlayOpacity != null) {
107 IntSetting.OVERLAY_OPACITY.setInt(overlayOpacity)
108 saveConfig = true
109 }
110
111 val overlayScale = preferences.migratePreference<Int>(Settings.PREF_CONTROL_SCALE)
112 if (overlayScale != null) {
113 IntSetting.OVERLAY_SCALE.setInt(overlayScale)
114 saveConfig = true
115 }
116
117 var setOverlayData = false
118 val overlayControlData = NativeConfig.getOverlayControlData()
119 if (overlayControlData.isEmpty()) {
120 val overlayControlDataMap =
121 NativeConfig.getOverlayControlData().associateBy { it.id }.toMutableMap()
122 for (button in Settings.overlayPreferences) {
123 val buttonId = convertButtonId(button)
124 var buttonEnabled = preferences.migratePreference<Boolean>(button)
125 if (buttonEnabled == null) {
126 buttonEnabled = OverlayControl.map[buttonId]?.defaultVisibility == true
127 }
128
129 var landscapeXPosition = preferences.migratePreference<Float>(
130 "$button-X${Settings.PREF_LANDSCAPE_SUFFIX}"
131 )?.toDouble()
132 var landscapeYPosition = preferences.migratePreference<Float>(
133 "$button-Y${Settings.PREF_LANDSCAPE_SUFFIX}"
134 )?.toDouble()
135 if (landscapeXPosition == null || landscapeYPosition == null) {
136 val landscapePosition = OverlayControl.map[buttonId]
137 ?.getDefaultPositionForLayout(OverlayLayout.Landscape) ?: Pair(0.0, 0.0)
138 landscapeXPosition = landscapePosition.first
139 landscapeYPosition = landscapePosition.second
140 }
141
142 var portraitXPosition = preferences.migratePreference<Float>(
143 "$button-X${Settings.PREF_PORTRAIT_SUFFIX}"
144 )?.toDouble()
145 var portraitYPosition = preferences.migratePreference<Float>(
146 "$button-Y${Settings.PREF_PORTRAIT_SUFFIX}"
147 )?.toDouble()
148 if (portraitXPosition == null || portraitYPosition == null) {
149 val portraitPosition = OverlayControl.map[buttonId]
150 ?.getDefaultPositionForLayout(OverlayLayout.Portrait) ?: Pair(0.0, 0.0)
151 portraitXPosition = portraitPosition.first
152 portraitYPosition = portraitPosition.second
153 }
154
155 var foldableXPosition = preferences.migratePreference<Float>(
156 "$button-X${Settings.PREF_FOLDABLE_SUFFIX}"
157 )?.toDouble()
158 var foldableYPosition = preferences.migratePreference<Float>(
159 "$button-Y${Settings.PREF_FOLDABLE_SUFFIX}"
160 )?.toDouble()
161 if (foldableXPosition == null || foldableYPosition == null) {
162 val foldablePosition = OverlayControl.map[buttonId]
163 ?.getDefaultPositionForLayout(OverlayLayout.Foldable) ?: Pair(0.0, 0.0)
164 foldableXPosition = foldablePosition.first
165 foldableYPosition = foldablePosition.second
166 }
167
168 val controlData = OverlayControlData(
169 buttonId,
170 buttonEnabled,
171 Pair(landscapeXPosition, landscapeYPosition),
172 Pair(portraitXPosition, portraitYPosition),
173 Pair(foldableXPosition, foldableYPosition)
174 )
175 overlayControlDataMap[buttonId] = controlData
176 setOverlayData = true
177 }
178
179 if (setOverlayData) {
180 NativeConfig.setOverlayControlData(
181 overlayControlDataMap.map { it.value }.toTypedArray()
182 )
183 saveConfig = true
184 }
185 }
186
187 if (saveConfig) {
188 NativeConfig.saveGlobalConfig()
189 }
190 }
191
192 private fun convertButtonId(buttonId: String): String =
193 when (buttonId) {
194 Settings.PREF_BUTTON_A -> OverlayControl.BUTTON_A.id
195 Settings.PREF_BUTTON_B -> OverlayControl.BUTTON_B.id
196 Settings.PREF_BUTTON_X -> OverlayControl.BUTTON_X.id
197 Settings.PREF_BUTTON_Y -> OverlayControl.BUTTON_Y.id
198 Settings.PREF_BUTTON_L -> OverlayControl.BUTTON_L.id
199 Settings.PREF_BUTTON_R -> OverlayControl.BUTTON_R.id
200 Settings.PREF_BUTTON_ZL -> OverlayControl.BUTTON_ZL.id
201 Settings.PREF_BUTTON_ZR -> OverlayControl.BUTTON_ZR.id
202 Settings.PREF_BUTTON_PLUS -> OverlayControl.BUTTON_PLUS.id
203 Settings.PREF_BUTTON_MINUS -> OverlayControl.BUTTON_MINUS.id
204 Settings.PREF_BUTTON_DPAD -> OverlayControl.COMBINED_DPAD.id
205 Settings.PREF_STICK_L -> OverlayControl.STICK_L.id
206 Settings.PREF_STICK_R -> OverlayControl.STICK_R.id
207 Settings.PREF_BUTTON_HOME -> OverlayControl.BUTTON_HOME.id
208 Settings.PREF_BUTTON_SCREENSHOT -> OverlayControl.BUTTON_CAPTURE.id
209 Settings.PREF_BUTTON_STICK_L -> OverlayControl.BUTTON_STICK_L.id
210 Settings.PREF_BUTTON_STICK_R -> OverlayControl.BUTTON_STICK_R.id
211 else -> ""
212 }
38} 213}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
deleted file mode 100644
index 7e8f058c1..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/EmulationMenuSettings.kt
+++ /dev/null
@@ -1,50 +0,0 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import androidx.preference.PreferenceManager
7import org.yuzu.yuzu_emu.YuzuApplication
8import org.yuzu.yuzu_emu.features.settings.model.Settings
9
10object EmulationMenuSettings {
11 private val preferences =
12 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
13
14 var joystickRelCenter: Boolean
15 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
16 set(value) {
17 preferences.edit()
18 .putBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, value)
19 .apply()
20 }
21 var dpadSlide: Boolean
22 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, true)
23 set(value) {
24 preferences.edit()
25 .putBoolean(Settings.PREF_MENU_SETTINGS_DPAD_SLIDE, value)
26 .apply()
27 }
28 var hapticFeedback: Boolean
29 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, false)
30 set(value) {
31 preferences.edit()
32 .putBoolean(Settings.PREF_MENU_SETTINGS_HAPTICS, value)
33 .apply()
34 }
35
36 var showFps: Boolean
37 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
38 set(value) {
39 preferences.edit()
40 .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, value)
41 .apply()
42 }
43 var showOverlay: Boolean
44 get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, true)
45 set(value) {
46 preferences.edit()
47 .putBoolean(Settings.PREF_MENU_SETTINGS_SHOW_OVERLAY, value)
48 .apply()
49 }
50}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
index 00c6bf90e..132f002fb 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt
@@ -21,6 +21,7 @@ import org.yuzu.yuzu_emu.model.TaskState
21import java.io.BufferedOutputStream 21import java.io.BufferedOutputStream
22import java.lang.NullPointerException 22import java.lang.NullPointerException
23import java.nio.charset.StandardCharsets 23import java.nio.charset.StandardCharsets
24import java.util.zip.Deflater
24import java.util.zip.ZipOutputStream 25import java.util.zip.ZipOutputStream
25import kotlin.IllegalStateException 26import kotlin.IllegalStateException
26 27
@@ -312,15 +313,23 @@ object FileUtil {
312 * @param inputFile File representation of the item that will be zipped 313 * @param inputFile File representation of the item that will be zipped
313 * @param rootDir Directory containing the inputFile 314 * @param rootDir Directory containing the inputFile
314 * @param outputStream Stream where the zip file will be output 315 * @param outputStream Stream where the zip file will be output
316 * @param cancelled [StateFlow] that reports whether this process has been cancelled
317 * @param compression Disables compression if true
315 */ 318 */
316 fun zipFromInternalStorage( 319 fun zipFromInternalStorage(
317 inputFile: File, 320 inputFile: File,
318 rootDir: String, 321 rootDir: String,
319 outputStream: BufferedOutputStream, 322 outputStream: BufferedOutputStream,
320 cancelled: StateFlow<Boolean>? = null 323 cancelled: StateFlow<Boolean>? = null,
324 compression: Boolean = true
321 ): TaskState { 325 ): TaskState {
322 try { 326 try {
323 ZipOutputStream(outputStream).use { zos -> 327 ZipOutputStream(outputStream).use { zos ->
328 if (!compression) {
329 zos.setMethod(ZipOutputStream.DEFLATED)
330 zos.setLevel(Deflater.NO_COMPRESSION)
331 }
332
324 inputFile.walkTopDown().forEach { file -> 333 inputFile.walkTopDown().forEach { file ->
325 if (cancelled?.value == true) { 334 if (cancelled?.value == true) {
326 return TaskState.Cancelled 335 return TaskState.Cancelled
@@ -338,6 +347,7 @@ object FileUtil {
338 } 347 }
339 } 348 }
340 } catch (e: Exception) { 349 } catch (e: Exception) {
350 Log.error("[FileUtil] Failed creating zip file - ${e.message}")
341 return TaskState.Failed 351 return TaskState.Failed
342 } 352 }
343 return TaskState.Completed 353 return TaskState.Completed
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 7512d5eed..a4c14b3a7 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,7 @@
4package org.yuzu.yuzu_emu.utils 4package org.yuzu.yuzu_emu.utils
5 5
6import org.yuzu.yuzu_emu.model.GameDir 6import org.yuzu.yuzu_emu.model.GameDir
7import org.yuzu.yuzu_emu.overlay.model.OverlayControlData
7 8
8object NativeConfig { 9object NativeConfig {
9 /** 10 /**
@@ -150,4 +151,21 @@ object NativeConfig {
150 */ 151 */
151 @Synchronized 152 @Synchronized
152 external fun setDisabledAddons(programId: String, disabledAddons: Array<String>) 153 external fun setDisabledAddons(programId: String, disabledAddons: Array<String>)
154
155 /**
156 * Gets an array of [OverlayControlData] from settings
157 *
158 * @return An array of [OverlayControlData]
159 */
160 @Synchronized
161 external fun getOverlayControlData(): Array<OverlayControlData>
162
163 /**
164 * Clears the AndroidSettings::values.overlay_control_data array and replaces its values
165 * with [overlayControlData]
166 *
167 * @param overlayControlData Replacement array of [OverlayControlData]
168 */
169 @Synchronized
170 external fun setOverlayControlData(overlayControlData: Array<OverlayControlData>)
153} 171}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
new file mode 100644
index 000000000..a233ba25c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/PreferenceUtil.kt
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4package org.yuzu.yuzu_emu.utils
5
6import android.content.SharedPreferences
7
8object PreferenceUtil {
9 /**
10 * Retrieves a shared preference value and then deletes the value in storage.
11 * @param key Associated key for the value in this preferences instance
12 * @return Typed value associated with [key]. Null if no such key exists.
13 */
14 inline fun <reified T> SharedPreferences.migratePreference(key: String): T? {
15 if (!this.contains(key)) {
16 return null
17 }
18
19 val value: Any = when (T::class) {
20 String::class -> this.getString(key, "")!!
21
22 Boolean::class -> this.getBoolean(key, false)
23
24 Int::class -> this.getInt(key, 0)
25
26 Float::class -> this.getFloat(key, 0f)
27
28 Long::class -> this.getLong(key, 0)
29
30 else -> throw IllegalStateException("Tried to migrate preference with invalid type!")
31 }
32 deletePreference(key)
33 return value as T
34 }
35
36 fun SharedPreferences.deletePreference(key: String) = this.edit().remove(key).apply()
37}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
index f312e24cf..6f7f40e43 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/ThemeHelper.kt
@@ -5,38 +5,38 @@ package org.yuzu.yuzu_emu.utils
5 5
6import android.content.res.Configuration 6import android.content.res.Configuration
7import android.graphics.Color 7import android.graphics.Color
8import android.os.Build
8import androidx.annotation.ColorInt 9import androidx.annotation.ColorInt
9import androidx.appcompat.app.AppCompatActivity 10import androidx.appcompat.app.AppCompatActivity
10import androidx.appcompat.app.AppCompatDelegate 11import androidx.appcompat.app.AppCompatDelegate
11import androidx.core.view.WindowCompat 12import androidx.core.view.WindowCompat
12import androidx.core.view.WindowInsetsControllerCompat 13import androidx.core.view.WindowInsetsControllerCompat
13import androidx.preference.PreferenceManager
14import kotlin.math.roundToInt 14import kotlin.math.roundToInt
15import org.yuzu.yuzu_emu.R 15import org.yuzu.yuzu_emu.R
16import org.yuzu.yuzu_emu.YuzuApplication 16import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
17import org.yuzu.yuzu_emu.features.settings.model.Settings 17import org.yuzu.yuzu_emu.features.settings.model.IntSetting
18import org.yuzu.yuzu_emu.ui.main.ThemeProvider 18import org.yuzu.yuzu_emu.ui.main.ThemeProvider
19 19
20object ThemeHelper { 20object ThemeHelper {
21 const val SYSTEM_BAR_ALPHA = 0.9f 21 const val SYSTEM_BAR_ALPHA = 0.9f
22 22
23 private const val DEFAULT = 0
24 private const val MATERIAL_YOU = 1
25
26 fun setTheme(activity: AppCompatActivity) { 23 fun setTheme(activity: AppCompatActivity) {
27 val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
28 setThemeMode(activity) 24 setThemeMode(activity)
29 when (preferences.getInt(Settings.PREF_THEME, 0)) { 25 when (Theme.from(IntSetting.THEME.getInt())) {
30 DEFAULT -> activity.setTheme(R.style.Theme_Yuzu_Main) 26 Theme.Default -> activity.setTheme(R.style.Theme_Yuzu_Main)
31 MATERIAL_YOU -> activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou) 27 Theme.MaterialYou -> {
28 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
29 activity.setTheme(R.style.Theme_Yuzu_Main_MaterialYou)
30 } else {
31 activity.setTheme(R.style.Theme_Yuzu_Main)
32 }
33 }
32 } 34 }
33 35
34 // Using a specific night mode check because this could apply incorrectly when using the 36 // Using a specific night mode check because this could apply incorrectly when using the
35 // light app mode, dark system mode, and black backgrounds. Launching the settings activity 37 // light app mode, dark system mode, and black backgrounds. Launching the settings activity
36 // will then show light mode colors/navigation bars but with black backgrounds. 38 // will then show light mode colors/navigation bars but with black backgrounds.
37 if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) && 39 if (BooleanSetting.BLACK_BACKGROUNDS.getBoolean() && isNightMode(activity)) {
38 isNightMode(activity)
39 ) {
40 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark) 40 activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
41 } 41 }
42 } 42 }
@@ -60,8 +60,7 @@ object ThemeHelper {
60 } 60 }
61 61
62 fun setThemeMode(activity: AppCompatActivity) { 62 fun setThemeMode(activity: AppCompatActivity) {
63 val themeMode = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext) 63 val themeMode = IntSetting.THEME_MODE.getInt()
64 .getInt(Settings.PREF_THEME_MODE, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
65 activity.delegate.localNightMode = themeMode 64 activity.delegate.localNightMode = themeMode
66 val windowController = WindowCompat.getInsetsController( 65 val windowController = WindowCompat.getInsetsController(
67 activity.window, 66 activity.window,
@@ -95,3 +94,12 @@ object ThemeHelper {
95 windowController.isAppearanceLightNavigationBars = false 94 windowController.isAppearanceLightNavigationBars = false
96 } 95 }
97} 96}
97
98enum class Theme(val int: Int) {
99 Default(0),
100 MaterialYou(1);
101
102 companion object {
103 fun from(int: Int): Theme = entries.firstOrNull { it.int == int } ?: Default
104 }
105}
diff --git a/src/android/app/src/main/jni/android_common/android_common.cpp b/src/android/app/src/main/jni/android_common/android_common.cpp
index 52d8ecfeb..1e884ffdd 100644
--- a/src/android/app/src/main/jni/android_common/android_common.cpp
+++ b/src/android/app/src/main/jni/android_common/android_common.cpp
@@ -9,6 +9,7 @@
9#include <jni.h> 9#include <jni.h>
10 10
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "jni/id_cache.h"
12 13
13std::string GetJString(JNIEnv* env, jstring jstr) { 14std::string GetJString(JNIEnv* env, jstring jstr) {
14 if (!jstr) { 15 if (!jstr) {
@@ -33,3 +34,11 @@ jstring ToJString(JNIEnv* env, std::string_view str) {
33jstring ToJString(JNIEnv* env, std::u16string_view str) { 34jstring ToJString(JNIEnv* env, std::u16string_view str) {
34 return ToJString(env, Common::UTF16ToUTF8(str)); 35 return ToJString(env, Common::UTF16ToUTF8(str));
35} 36}
37
38double GetJDouble(JNIEnv* env, jobject jdouble) {
39 return env->GetDoubleField(jdouble, IDCache::GetDoubleValueField());
40}
41
42jobject ToJDouble(JNIEnv* env, double value) {
43 return env->NewObject(IDCache::GetDoubleClass(), IDCache::GetDoubleConstructor(), value);
44}
diff --git a/src/android/app/src/main/jni/android_common/android_common.h b/src/android/app/src/main/jni/android_common/android_common.h
index ccb0c06f7..8eb803e1b 100644
--- a/src/android/app/src/main/jni/android_common/android_common.h
+++ b/src/android/app/src/main/jni/android_common/android_common.h
@@ -10,3 +10,6 @@
10std::string GetJString(JNIEnv* env, jstring jstr); 10std::string GetJString(JNIEnv* env, jstring jstr);
11jstring ToJString(JNIEnv* env, std::string_view str); 11jstring ToJString(JNIEnv* env, std::string_view str);
12jstring ToJString(JNIEnv* env, std::u16string_view str); 12jstring ToJString(JNIEnv* env, std::u16string_view str);
13
14double GetJDouble(JNIEnv* env, jobject jdouble);
15jobject ToJDouble(JNIEnv* env, double value);
diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp
index 9c3a5a9b2..08aed3216 100644
--- a/src/android/app/src/main/jni/android_config.cpp
+++ b/src/android/app/src/main/jni/android_config.cpp
@@ -14,12 +14,6 @@ AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_t
14 } 14 }
15} 15}
16 16
17AndroidConfig::~AndroidConfig() {
18 if (global) {
19 AndroidConfig::SaveAllValues();
20 }
21}
22
23void AndroidConfig::ReloadAllValues() { 17void AndroidConfig::ReloadAllValues() {
24 Reload(); 18 Reload();
25 ReadAndroidValues(); 19 ReadAndroidValues();
@@ -35,6 +29,7 @@ void AndroidConfig::ReadAndroidValues() {
35 if (global) { 29 if (global) {
36 ReadAndroidUIValues(); 30 ReadAndroidUIValues();
37 ReadUIValues(); 31 ReadUIValues();
32 ReadOverlayValues();
38 } 33 }
39 ReadDriverValues(); 34 ReadDriverValues();
40} 35}
@@ -81,10 +76,42 @@ void AndroidConfig::ReadDriverValues() {
81 EndGroup(); 76 EndGroup();
82} 77}
83 78
79void AndroidConfig::ReadOverlayValues() {
80 BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
81
82 ReadCategory(Settings::Category::Overlay);
83
84 AndroidSettings::values.overlay_control_data.clear();
85 const int control_data_size = BeginArray("control_data");
86 for (int i = 0; i < control_data_size; ++i) {
87 SetArrayIndex(i);
88 AndroidSettings::OverlayControlData control_data;
89 control_data.id = ReadStringSetting(std::string("id"));
90 control_data.enabled = ReadBooleanSetting(std::string("enabled"));
91 control_data.landscape_position.first =
92 ReadDoubleSetting(std::string("landscape\\x_position"));
93 control_data.landscape_position.second =
94 ReadDoubleSetting(std::string("landscape\\y_position"));
95 control_data.portrait_position.first =
96 ReadDoubleSetting(std::string("portrait\\x_position"));
97 control_data.portrait_position.second =
98 ReadDoubleSetting(std::string("portrait\\y_position"));
99 control_data.foldable_position.first =
100 ReadDoubleSetting(std::string("foldable\\x_position"));
101 control_data.foldable_position.second =
102 ReadDoubleSetting(std::string("foldable\\y_position"));
103 AndroidSettings::values.overlay_control_data.push_back(control_data);
104 }
105 EndArray();
106
107 EndGroup();
108}
109
84void AndroidConfig::SaveAndroidValues() { 110void AndroidConfig::SaveAndroidValues() {
85 if (global) { 111 if (global) {
86 SaveAndroidUIValues(); 112 SaveAndroidUIValues();
87 SaveUIValues(); 113 SaveUIValues();
114 SaveOverlayValues();
88 } 115 }
89 SaveDriverValues(); 116 SaveDriverValues();
90 117
@@ -114,8 +141,9 @@ void AndroidConfig::SavePathValues() {
114 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) { 141 for (size_t i = 0; i < AndroidSettings::values.game_dirs.size(); ++i) {
115 SetArrayIndex(i); 142 SetArrayIndex(i);
116 const auto& game_dir = AndroidSettings::values.game_dirs[i]; 143 const auto& game_dir = AndroidSettings::values.game_dirs[i];
117 WriteSetting(std::string("path"), game_dir.path); 144 WriteStringSetting(std::string("path"), game_dir.path);
118 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); 145 WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
146 std::make_optional(false));
119 } 147 }
120 EndArray(); 148 EndArray();
121 149
@@ -130,6 +158,35 @@ void AndroidConfig::SaveDriverValues() {
130 EndGroup(); 158 EndGroup();
131} 159}
132 160
161void AndroidConfig::SaveOverlayValues() {
162 BeginGroup(Settings::TranslateCategory(Settings::Category::Overlay));
163
164 WriteCategory(Settings::Category::Overlay);
165
166 BeginArray("control_data");
167 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
168 SetArrayIndex(i);
169 const auto& control_data = AndroidSettings::values.overlay_control_data[i];
170 WriteStringSetting(std::string("id"), control_data.id);
171 WriteBooleanSetting(std::string("enabled"), control_data.enabled);
172 WriteDoubleSetting(std::string("landscape\\x_position"),
173 control_data.landscape_position.first);
174 WriteDoubleSetting(std::string("landscape\\y_position"),
175 control_data.landscape_position.second);
176 WriteDoubleSetting(std::string("portrait\\x_position"),
177 control_data.portrait_position.first);
178 WriteDoubleSetting(std::string("portrait\\y_position"),
179 control_data.portrait_position.second);
180 WriteDoubleSetting(std::string("foldable\\x_position"),
181 control_data.foldable_position.first);
182 WriteDoubleSetting(std::string("foldable\\y_position"),
183 control_data.foldable_position.second);
184 }
185 EndArray();
186
187 EndGroup();
188}
189
133std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) { 190std::vector<Settings::BasicSetting*>& AndroidConfig::FindRelevantList(Settings::Category category) {
134 auto& map = Settings::values.linkage.by_category; 191 auto& map = Settings::values.linkage.by_category;
135 if (map.contains(category)) { 192 if (map.contains(category)) {
diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h
index 2c12874e1..693e1e3f0 100644
--- a/src/android/app/src/main/jni/android_config.h
+++ b/src/android/app/src/main/jni/android_config.h
@@ -9,7 +9,6 @@ class AndroidConfig final : public Config {
9public: 9public:
10 explicit AndroidConfig(const std::string& config_name = "config", 10 explicit AndroidConfig(const std::string& config_name = "config",
11 ConfigType config_type = ConfigType::GlobalConfig); 11 ConfigType config_type = ConfigType::GlobalConfig);
12 ~AndroidConfig() override;
13 12
14 void ReloadAllValues() override; 13 void ReloadAllValues() override;
15 void SaveAllValues() override; 14 void SaveAllValues() override;
@@ -18,6 +17,7 @@ protected:
18 void ReadAndroidValues(); 17 void ReadAndroidValues();
19 void ReadAndroidUIValues(); 18 void ReadAndroidUIValues();
20 void ReadDriverValues(); 19 void ReadDriverValues();
20 void ReadOverlayValues();
21 void ReadHidbusValues() override {} 21 void ReadHidbusValues() override {}
22 void ReadDebugControlValues() override {} 22 void ReadDebugControlValues() override {}
23 void ReadPathValues() override; 23 void ReadPathValues() override;
@@ -30,6 +30,7 @@ protected:
30 void SaveAndroidValues(); 30 void SaveAndroidValues();
31 void SaveAndroidUIValues(); 31 void SaveAndroidUIValues();
32 void SaveDriverValues(); 32 void SaveDriverValues();
33 void SaveOverlayValues();
33 void SaveHidbusValues() override {} 34 void SaveHidbusValues() override {}
34 void SaveDebugControlValues() override {} 35 void SaveDebugControlValues() override {}
35 void SavePathValues() override; 36 void SavePathValues() override;
diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h
index 3733f5a3c..559ae83eb 100644
--- a/src/android/app/src/main/jni/android_settings.h
+++ b/src/android/app/src/main/jni/android_settings.h
@@ -14,6 +14,14 @@ struct GameDir {
14 bool deep_scan = false; 14 bool deep_scan = false;
15}; 15};
16 16
17struct OverlayControlData {
18 std::string id;
19 bool enabled;
20 std::pair<double, double> landscape_position;
21 std::pair<double, double> portrait_position;
22 std::pair<double, double> foldable_position;
23};
24
17struct Values { 25struct Values {
18 Settings::Linkage linkage; 26 Settings::Linkage linkage;
19 27
@@ -33,6 +41,28 @@ struct Values {
33 41
34 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path", 42 Settings::SwitchableSetting<std::string, false> driver_path{linkage, "", "driver_path",
35 Settings::Category::GpuDriver}; 43 Settings::Category::GpuDriver};
44
45 Settings::Setting<s32> theme{linkage, 0, "theme", Settings::Category::Android};
46 Settings::Setting<s32> theme_mode{linkage, -1, "theme_mode", Settings::Category::Android};
47 Settings::Setting<bool> black_backgrounds{linkage, false, "black_backgrounds",
48 Settings::Category::Android};
49
50 // Input/performance overlay settings
51 std::vector<OverlayControlData> overlay_control_data;
52 Settings::Setting<s32> overlay_scale{linkage, 50, "control_scale", Settings::Category::Overlay};
53 Settings::Setting<s32> overlay_opacity{linkage, 100, "control_opacity",
54 Settings::Category::Overlay};
55
56 Settings::Setting<bool> joystick_rel_center{linkage, true, "joystick_rel_center",
57 Settings::Category::Overlay};
58 Settings::Setting<bool> dpad_slide{linkage, true, "dpad_slide", Settings::Category::Overlay};
59 Settings::Setting<bool> haptic_feedback{linkage, true, "haptic_feedback",
60 Settings::Category::Overlay};
61 Settings::Setting<bool> show_performance_overlay{linkage, true, "show_performance_overlay",
62 Settings::Category::Overlay};
63 Settings::Setting<bool> show_input_overlay{linkage, true, "show_input_overlay",
64 Settings::Category::Overlay};
65 Settings::Setting<bool> touchscreen{linkage, true, "touchscreen", Settings::Category::Overlay};
36}; 66};
37 67
38extern Values values; 68extern Values values;
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index e7a86d3fd..c79ad7d76 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -35,6 +35,18 @@ static jmethodID s_pair_constructor;
35static jfieldID s_pair_first_field; 35static jfieldID s_pair_first_field;
36static jfieldID s_pair_second_field; 36static jfieldID s_pair_second_field;
37 37
38static jclass s_overlay_control_data_class;
39static jmethodID s_overlay_control_data_constructor;
40static jfieldID s_overlay_control_data_id_field;
41static jfieldID s_overlay_control_data_enabled_field;
42static jfieldID s_overlay_control_data_landscape_position_field;
43static jfieldID s_overlay_control_data_portrait_position_field;
44static jfieldID s_overlay_control_data_foldable_position_field;
45
46static jclass s_double_class;
47static jmethodID s_double_constructor;
48static jfieldID s_double_value_field;
49
38static constexpr jint JNI_VERSION = JNI_VERSION_1_6; 50static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
39 51
40namespace IDCache { 52namespace IDCache {
@@ -146,6 +158,46 @@ jfieldID GetPairSecondField() {
146 return s_pair_second_field; 158 return s_pair_second_field;
147} 159}
148 160
161jclass GetOverlayControlDataClass() {
162 return s_overlay_control_data_class;
163}
164
165jmethodID GetOverlayControlDataConstructor() {
166 return s_overlay_control_data_constructor;
167}
168
169jfieldID GetOverlayControlDataIdField() {
170 return s_overlay_control_data_id_field;
171}
172
173jfieldID GetOverlayControlDataEnabledField() {
174 return s_overlay_control_data_enabled_field;
175}
176
177jfieldID GetOverlayControlDataLandscapePositionField() {
178 return s_overlay_control_data_landscape_position_field;
179}
180
181jfieldID GetOverlayControlDataPortraitPositionField() {
182 return s_overlay_control_data_portrait_position_field;
183}
184
185jfieldID GetOverlayControlDataFoldablePositionField() {
186 return s_overlay_control_data_foldable_position_field;
187}
188
189jclass GetDoubleClass() {
190 return s_double_class;
191}
192
193jmethodID GetDoubleConstructor() {
194 return s_double_constructor;
195}
196
197jfieldID GetDoubleValueField() {
198 return s_double_value_field;
199}
200
149} // namespace IDCache 201} // namespace IDCache
150 202
151#ifdef __cplusplus 203#ifdef __cplusplus
@@ -207,6 +259,31 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
207 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;"); 259 s_pair_second_field = env->GetFieldID(pair_class, "second", "Ljava/lang/Object;");
208 env->DeleteLocalRef(pair_class); 260 env->DeleteLocalRef(pair_class);
209 261
262 const jclass overlay_control_data_class =
263 env->FindClass("org/yuzu/yuzu_emu/overlay/model/OverlayControlData");
264 s_overlay_control_data_class =
265 reinterpret_cast<jclass>(env->NewGlobalRef(overlay_control_data_class));
266 s_overlay_control_data_constructor =
267 env->GetMethodID(overlay_control_data_class, "<init>",
268 "(Ljava/lang/String;ZLkotlin/Pair;Lkotlin/Pair;Lkotlin/Pair;)V");
269 s_overlay_control_data_id_field =
270 env->GetFieldID(overlay_control_data_class, "id", "Ljava/lang/String;");
271 s_overlay_control_data_enabled_field =
272 env->GetFieldID(overlay_control_data_class, "enabled", "Z");
273 s_overlay_control_data_landscape_position_field =
274 env->GetFieldID(overlay_control_data_class, "landscapePosition", "Lkotlin/Pair;");
275 s_overlay_control_data_portrait_position_field =
276 env->GetFieldID(overlay_control_data_class, "portraitPosition", "Lkotlin/Pair;");
277 s_overlay_control_data_foldable_position_field =
278 env->GetFieldID(overlay_control_data_class, "foldablePosition", "Lkotlin/Pair;");
279 env->DeleteLocalRef(overlay_control_data_class);
280
281 const jclass double_class = env->FindClass("java/lang/Double");
282 s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
283 s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");
284 s_double_value_field = env->GetFieldID(double_class, "value", "D");
285 env->DeleteLocalRef(double_class);
286
210 // Initialize Android Storage 287 // Initialize Android Storage
211 Common::FS::Android::RegisterCallbacks(env, s_native_library_class); 288 Common::FS::Android::RegisterCallbacks(env, s_native_library_class);
212 289
@@ -231,6 +308,8 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
231 env->DeleteGlobalRef(s_game_class); 308 env->DeleteGlobalRef(s_game_class);
232 env->DeleteGlobalRef(s_string_class); 309 env->DeleteGlobalRef(s_string_class);
233 env->DeleteGlobalRef(s_pair_class); 310 env->DeleteGlobalRef(s_pair_class);
311 env->DeleteGlobalRef(s_overlay_control_data_class);
312 env->DeleteGlobalRef(s_double_class);
234 313
235 // UnInitialize applets 314 // UnInitialize applets
236 SoftwareKeyboard::CleanupJNI(env); 315 SoftwareKeyboard::CleanupJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index 24030be42..784d1412f 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -35,4 +35,16 @@ jmethodID GetPairConstructor();
35jfieldID GetPairFirstField(); 35jfieldID GetPairFirstField();
36jfieldID GetPairSecondField(); 36jfieldID GetPairSecondField();
37 37
38jclass GetOverlayControlDataClass();
39jmethodID GetOverlayControlDataConstructor();
40jfieldID GetOverlayControlDataIdField();
41jfieldID GetOverlayControlDataEnabledField();
42jfieldID GetOverlayControlDataLandscapePositionField();
43jfieldID GetOverlayControlDataPortraitPositionField();
44jfieldID GetOverlayControlDataFoldablePositionField();
45
46jclass GetDoubleClass();
47jmethodID GetDoubleConstructor();
48jfieldID GetDoubleValueField();
49
38} // namespace IDCache 50} // namespace IDCache
diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp
index 324d9e9cd..535902483 100644
--- a/src/android/app/src/main/jni/native_config.cpp
+++ b/src/android/app/src/main/jni/native_config.cpp
@@ -344,4 +344,74 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, j
344 Settings::values.disabled_addons[program_id] = disabled_addons; 344 Settings::values.disabled_addons[program_id] = disabled_addons;
345} 345}
346 346
347jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getOverlayControlData(JNIEnv* env,
348 jobject obj) {
349 jobjectArray joverlayControlDataArray =
350 env->NewObjectArray(AndroidSettings::values.overlay_control_data.size(),
351 IDCache::GetOverlayControlDataClass(), nullptr);
352 for (size_t i = 0; i < AndroidSettings::values.overlay_control_data.size(); ++i) {
353 const auto& control_data = AndroidSettings::values.overlay_control_data[i];
354 jobject jlandscapePosition =
355 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
356 ToJDouble(env, control_data.landscape_position.first),
357 ToJDouble(env, control_data.landscape_position.second));
358 jobject jportraitPosition =
359 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
360 ToJDouble(env, control_data.portrait_position.first),
361 ToJDouble(env, control_data.portrait_position.second));
362 jobject jfoldablePosition =
363 env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
364 ToJDouble(env, control_data.foldable_position.first),
365 ToJDouble(env, control_data.foldable_position.second));
366
367 jobject jcontrolData = env->NewObject(
368 IDCache::GetOverlayControlDataClass(), IDCache::GetOverlayControlDataConstructor(),
369 ToJString(env, control_data.id), control_data.enabled, jlandscapePosition,
370 jportraitPosition, jfoldablePosition);
371 env->SetObjectArrayElement(joverlayControlDataArray, i, jcontrolData);
372 }
373 return joverlayControlDataArray;
374}
375
376void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setOverlayControlData(
377 JNIEnv* env, jobject obj, jobjectArray joverlayControlDataArray) {
378 AndroidSettings::values.overlay_control_data.clear();
379 int size = env->GetArrayLength(joverlayControlDataArray);
380
381 if (size == 0) {
382 return;
383 }
384
385 for (int i = 0; i < size; ++i) {
386 jobject joverlayControlData = env->GetObjectArrayElement(joverlayControlDataArray, i);
387 jstring jidString = static_cast<jstring>(
388 env->GetObjectField(joverlayControlData, IDCache::GetOverlayControlDataIdField()));
389 bool enabled = static_cast<bool>(env->GetBooleanField(
390 joverlayControlData, IDCache::GetOverlayControlDataEnabledField()));
391
392 jobject jlandscapePosition = env->GetObjectField(
393 joverlayControlData, IDCache::GetOverlayControlDataLandscapePositionField());
394 std::pair<double, double> landscape_position = std::make_pair(
395 GetJDouble(env, env->GetObjectField(jlandscapePosition, IDCache::GetPairFirstField())),
396 GetJDouble(env,
397 env->GetObjectField(jlandscapePosition, IDCache::GetPairSecondField())));
398
399 jobject jportraitPosition = env->GetObjectField(
400 joverlayControlData, IDCache::GetOverlayControlDataPortraitPositionField());
401 std::pair<double, double> portrait_position = std::make_pair(
402 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairFirstField())),
403 GetJDouble(env, env->GetObjectField(jportraitPosition, IDCache::GetPairSecondField())));
404
405 jobject jfoldablePosition = env->GetObjectField(
406 joverlayControlData, IDCache::GetOverlayControlDataFoldablePositionField());
407 std::pair<double, double> foldable_position = std::make_pair(
408 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairFirstField())),
409 GetJDouble(env, env->GetObjectField(jfoldablePosition, IDCache::GetPairSecondField())));
410
411 AndroidSettings::values.overlay_control_data.push_back(AndroidSettings::OverlayControlData{
412 GetJString(env, jidString), enabled, landscape_position, portrait_position,
413 foldable_position});
414 }
415}
416
347} // extern "C" 417} // extern "C"
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 5cb84182e..1c08e2e1b 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
@@ -24,7 +24,7 @@
24 android:layout_width="0dp" 24 android:layout_width="0dp"
25 android:layout_height="wrap_content" 25 android:layout_height="wrap_content"
26 android:layout_marginEnd="24dp" 26 android:layout_marginEnd="24dp"
27 android:gravity="center_vertical" 27 android:layout_gravity="center_vertical"
28 android:orientation="vertical" 28 android:orientation="vertical"
29 android:layout_weight="1"> 29 android:layout_weight="1">
30 30
diff --git a/src/android/app/src/main/res/menu/menu_overlay_options.xml b/src/android/app/src/main/res/menu/menu_overlay_options.xml
index 4885b4f6f..363781652 100644
--- a/src/android/app/src/main/res/menu/menu_overlay_options.xml
+++ b/src/android/app/src/main/res/menu/menu_overlay_options.xml
@@ -39,6 +39,11 @@
39 android:checkable="true" /> 39 android:checkable="true" />
40 40
41 <item 41 <item
42 android:id="@+id/menu_touchscreen"
43 android:title="@string/touchscreen"
44 android:checkable="true" />
45
46 <item
42 android:id="@+id/menu_reset_overlay" 47 android:id="@+id/menu_reset_overlay"
43 android:title="@string/emulation_touch_overlay_reset" /> 48 android:title="@string/emulation_touch_overlay_reset" />
44 49
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index e3915ef4f..0363ff3b6 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -118,15 +118,23 @@
118 </integer-array> 118 </integer-array>
119 119
120 <string-array name="rendererScreenLayoutNames"> 120 <string-array name="rendererScreenLayoutNames">
121 <item>@string/screen_layout_auto</item>
122 <item>@string/screen_layout_sensor_landscape</item>
121 <item>@string/screen_layout_landscape</item> 123 <item>@string/screen_layout_landscape</item>
124 <item>@string/screen_layout_reverse_landscape</item>
125 <item>@string/screen_layout_sensor_portrait</item>
122 <item>@string/screen_layout_portrait</item> 126 <item>@string/screen_layout_portrait</item>
123 <item>@string/screen_layout_auto</item> 127 <item>@string/screen_layout_reverse_portrait</item>
124 </string-array> 128 </string-array>
125 129
126 <integer-array name="rendererScreenLayoutValues"> 130 <integer-array name="rendererScreenLayoutValues">
131 <item>0</item>
127 <item>5</item> 132 <item>5</item>
133 <item>1</item>
134 <item>2</item>
135 <item>6</item>
128 <item>4</item> 136 <item>4</item>
129 <item>0</item> 137 <item>3</item>
130 </integer-array> 138 </integer-array>
131 139
132 <string-array name="rendererAspectRatioNames"> 140 <string-array name="rendererAspectRatioNames">
@@ -212,19 +220,19 @@
212 <item>B</item> 220 <item>B</item>
213 <item>X</item> 221 <item>X</item>
214 <item>Y</item> 222 <item>Y</item>
223 <item>+</item>
224 <item>-</item>
225 <item>@string/gamepad_home</item>
226 <item>@string/gamepad_screenshot</item>
215 <item>L</item> 227 <item>L</item>
216 <item>R</item> 228 <item>R</item>
217 <item>ZL</item> 229 <item>ZL</item>
218 <item>ZR</item> 230 <item>ZR</item>
219 <item>+</item>
220 <item>-</item>
221 <item>@string/gamepad_d_pad</item>
222 <item>@string/gamepad_left_stick</item> 231 <item>@string/gamepad_left_stick</item>
223 <item>@string/gamepad_right_stick</item> 232 <item>@string/gamepad_right_stick</item>
224 <item>L3</item> 233 <item>L3</item>
225 <item>R3</item> 234 <item>R3</item>
226 <item>@string/gamepad_home</item> 235 <item>@string/gamepad_d_pad</item>
227 <item>@string/gamepad_screenshot</item>
228 </string-array> 236 </string-array>
229 237
230 <string-array name="themeEntries"> 238 <string-array name="themeEntries">
@@ -267,4 +275,21 @@
267 <item>3</item> 275 <item>3</item>
268 </integer-array> 276 </integer-array>
269 277
278 <string-array name="anisoEntries">
279 <item>@string/auto</item>
280 <item>@string/slider_default</item>
281 <item>@string/multiplier_two</item>
282 <item>@string/multiplier_four</item>
283 <item>@string/multiplier_eight</item>
284 <item>@string/multiplier_sixteen</item>
285 </string-array>
286 <integer-array name="anisoValues">
287 <item>0</item>
288 <item>1</item>
289 <item>2</item>
290 <item>3</item>
291 <item>4</item>
292 <item>5</item>
293 </integer-array>
294
270</resources> 295</resources>
diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml
index dc527965c..1c6f5db93 100644
--- a/src/android/app/src/main/res/values/integers.xml
+++ b/src/android/app/src/main/res/values/integers.xml
@@ -3,111 +3,111 @@
3 <integer name="grid_columns">1</integer> 3 <integer name="grid_columns">1</integer>
4 4
5 <!-- Default SWITCH landscape layout --> 5 <!-- Default SWITCH landscape layout -->
6 <integer name="SWITCH_BUTTON_A_X">760</integer> 6 <integer name="BUTTON_A_X">760</integer>
7 <integer name="SWITCH_BUTTON_A_Y">790</integer> 7 <integer name="BUTTON_A_Y">790</integer>
8 <integer name="SWITCH_BUTTON_B_X">710</integer> 8 <integer name="BUTTON_B_X">710</integer>
9 <integer name="SWITCH_BUTTON_B_Y">900</integer> 9 <integer name="BUTTON_B_Y">900</integer>
10 <integer name="SWITCH_BUTTON_X_X">710</integer> 10 <integer name="BUTTON_X_X">710</integer>
11 <integer name="SWITCH_BUTTON_X_Y">680</integer> 11 <integer name="BUTTON_X_Y">680</integer>
12 <integer name="SWITCH_BUTTON_Y_X">660</integer> 12 <integer name="BUTTON_Y_X">660</integer>
13 <integer name="SWITCH_BUTTON_Y_Y">790</integer> 13 <integer name="BUTTON_Y_Y">790</integer>
14 <integer name="SWITCH_STICK_L_X">100</integer> 14 <integer name="BUTTON_PLUS_X">540</integer>
15 <integer name="SWITCH_STICK_L_Y">670</integer> 15 <integer name="BUTTON_PLUS_Y">950</integer>
16 <integer name="SWITCH_STICK_R_X">900</integer> 16 <integer name="BUTTON_MINUS_X">460</integer>
17 <integer name="SWITCH_STICK_R_Y">670</integer> 17 <integer name="BUTTON_MINUS_Y">950</integer>
18 <integer name="SWITCH_TRIGGER_L_X">70</integer> 18 <integer name="BUTTON_HOME_X">600</integer>
19 <integer name="SWITCH_TRIGGER_L_Y">220</integer> 19 <integer name="BUTTON_HOME_Y">950</integer>
20 <integer name="SWITCH_TRIGGER_R_X">930</integer> 20 <integer name="BUTTON_CAPTURE_X">400</integer>
21 <integer name="SWITCH_TRIGGER_R_Y">220</integer> 21 <integer name="BUTTON_CAPTURE_Y">950</integer>
22 <integer name="SWITCH_TRIGGER_ZL_X">70</integer> 22 <integer name="BUTTON_L_X">70</integer>
23 <integer name="SWITCH_TRIGGER_ZL_Y">90</integer> 23 <integer name="BUTTON_L_Y">220</integer>
24 <integer name="SWITCH_TRIGGER_ZR_X">930</integer> 24 <integer name="BUTTON_R_X">930</integer>
25 <integer name="SWITCH_TRIGGER_ZR_Y">90</integer> 25 <integer name="BUTTON_R_Y">220</integer>
26 <integer name="SWITCH_BUTTON_MINUS_X">460</integer> 26 <integer name="BUTTON_ZL_X">70</integer>
27 <integer name="SWITCH_BUTTON_MINUS_Y">950</integer> 27 <integer name="BUTTON_ZL_Y">90</integer>
28 <integer name="SWITCH_BUTTON_PLUS_X">540</integer> 28 <integer name="BUTTON_ZR_X">930</integer>
29 <integer name="SWITCH_BUTTON_PLUS_Y">950</integer> 29 <integer name="BUTTON_ZR_Y">90</integer>
30 <integer name="SWITCH_BUTTON_HOME_X">600</integer> 30 <integer name="BUTTON_STICK_L_X">870</integer>
31 <integer name="SWITCH_BUTTON_HOME_Y">950</integer> 31 <integer name="BUTTON_STICK_L_Y">400</integer>
32 <integer name="SWITCH_BUTTON_CAPTURE_X">400</integer> 32 <integer name="BUTTON_STICK_R_X">960</integer>
33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> 33 <integer name="BUTTON_STICK_R_Y">430</integer>
34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer> 34 <integer name="STICK_L_X">100</integer>
35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> 35 <integer name="STICK_L_Y">670</integer>
36 <integer name="SWITCH_BUTTON_STICK_L_X">870</integer> 36 <integer name="STICK_R_X">900</integer>
37 <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer> 37 <integer name="STICK_R_Y">670</integer>
38 <integer name="SWITCH_BUTTON_STICK_R_X">960</integer> 38 <integer name="COMBINED_DPAD_X">260</integer>
39 <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer> 39 <integer name="COMBINED_DPAD_Y">790</integer>
40 40
41 <!-- Default SWITCH portrait layout --> 41 <!-- Default SWITCH portrait layout -->
42 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> 42 <integer name="BUTTON_A_X_PORTRAIT">840</integer>
43 <integer name="SWITCH_BUTTON_A_Y_PORTRAIT">840</integer> 43 <integer name="BUTTON_A_Y_PORTRAIT">840</integer>
44 <integer name="SWITCH_BUTTON_B_X_PORTRAIT">740</integer> 44 <integer name="BUTTON_B_X_PORTRAIT">740</integer>
45 <integer name="SWITCH_BUTTON_B_Y_PORTRAIT">880</integer> 45 <integer name="BUTTON_B_Y_PORTRAIT">880</integer>
46 <integer name="SWITCH_BUTTON_X_X_PORTRAIT">740</integer> 46 <integer name="BUTTON_X_X_PORTRAIT">740</integer>
47 <integer name="SWITCH_BUTTON_X_Y_PORTRAIT">800</integer> 47 <integer name="BUTTON_X_Y_PORTRAIT">800</integer>
48 <integer name="SWITCH_BUTTON_Y_X_PORTRAIT">640</integer> 48 <integer name="BUTTON_Y_X_PORTRAIT">640</integer>
49 <integer name="SWITCH_BUTTON_Y_Y_PORTRAIT">840</integer> 49 <integer name="BUTTON_Y_Y_PORTRAIT">840</integer>
50 <integer name="SWITCH_STICK_L_X_PORTRAIT">180</integer> 50 <integer name="BUTTON_PLUS_Y_PORTRAIT">950</integer>
51 <integer name="SWITCH_STICK_L_Y_PORTRAIT">660</integer> 51 <integer name="BUTTON_MINUS_X_PORTRAIT">440</integer>
52 <integer name="SWITCH_STICK_R_X_PORTRAIT">820</integer> 52 <integer name="BUTTON_MINUS_Y_PORTRAIT">950</integer>
53 <integer name="SWITCH_STICK_R_Y_PORTRAIT">660</integer> 53 <integer name="BUTTON_HOME_X_PORTRAIT">680</integer>
54 <integer name="SWITCH_TRIGGER_L_X_PORTRAIT">140</integer> 54 <integer name="BUTTON_HOME_Y_PORTRAIT">950</integer>
55 <integer name="SWITCH_TRIGGER_L_Y_PORTRAIT">260</integer> 55 <integer name="BUTTON_CAPTURE_X_PORTRAIT">320</integer>
56 <integer name="SWITCH_TRIGGER_R_X_PORTRAIT">860</integer> 56 <integer name="BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
57 <integer name="SWITCH_TRIGGER_R_Y_PORTRAIT">260</integer> 57 <integer name="BUTTON_L_X_PORTRAIT">140</integer>
58 <integer name="SWITCH_TRIGGER_ZL_X_PORTRAIT">140</integer> 58 <integer name="BUTTON_L_Y_PORTRAIT">260</integer>
59 <integer name="SWITCH_TRIGGER_ZL_Y_PORTRAIT">200</integer> 59 <integer name="BUTTON_R_X_PORTRAIT">860</integer>
60 <integer name="SWITCH_TRIGGER_ZR_X_PORTRAIT">860</integer> 60 <integer name="BUTTON_R_Y_PORTRAIT">260</integer>
61 <integer name="SWITCH_TRIGGER_ZR_Y_PORTRAIT">200</integer> 61 <integer name="BUTTON_ZL_X_PORTRAIT">140</integer>
62 <integer name="SWITCH_BUTTON_MINUS_X_PORTRAIT">440</integer> 62 <integer name="BUTTON_ZL_Y_PORTRAIT">200</integer>
63 <integer name="SWITCH_BUTTON_MINUS_Y_PORTRAIT">950</integer> 63 <integer name="BUTTON_ZR_X_PORTRAIT">860</integer>
64 <integer name="SWITCH_BUTTON_PLUS_X_PORTRAIT">560</integer> 64 <integer name="BUTTON_ZR_Y_PORTRAIT">200</integer>
65 <integer name="SWITCH_BUTTON_PLUS_Y_PORTRAIT">950</integer> 65 <integer name="BUTTON_PLUS_X_PORTRAIT">560</integer>
66 <integer name="SWITCH_BUTTON_HOME_X_PORTRAIT">680</integer> 66 <integer name="BUTTON_STICK_L_X_PORTRAIT">730</integer>
67 <integer name="SWITCH_BUTTON_HOME_Y_PORTRAIT">950</integer> 67 <integer name="BUTTON_STICK_L_Y_PORTRAIT">510</integer>
68 <integer name="SWITCH_BUTTON_CAPTURE_X_PORTRAIT">320</integer> 68 <integer name="BUTTON_STICK_R_X_PORTRAIT">900</integer>
69 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> 69 <integer name="BUTTON_STICK_R_Y_PORTRAIT">540</integer>
70 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> 70 <integer name="STICK_L_X_PORTRAIT">180</integer>
71 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> 71 <integer name="STICK_L_Y_PORTRAIT">660</integer>
72 <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer> 72 <integer name="STICK_R_X_PORTRAIT">820</integer>
73 <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer> 73 <integer name="STICK_R_Y_PORTRAIT">660</integer>
74 <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer> 74 <integer name="COMBINED_DPAD_X_PORTRAIT">240</integer>
75 <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer> 75 <integer name="COMBINED_DPAD_Y_PORTRAIT">840</integer>
76 76
77 <!-- Default SWITCH foldable layout --> 77 <!-- Default SWITCH foldable layout -->
78 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> 78 <integer name="BUTTON_A_X_FOLDABLE">840</integer>
79 <integer name="SWITCH_BUTTON_A_Y_FOLDABLE">390</integer> 79 <integer name="BUTTON_A_Y_FOLDABLE">390</integer>
80 <integer name="SWITCH_BUTTON_B_X_FOLDABLE">740</integer> 80 <integer name="BUTTON_B_X_FOLDABLE">740</integer>
81 <integer name="SWITCH_BUTTON_B_Y_FOLDABLE">430</integer> 81 <integer name="BUTTON_B_Y_FOLDABLE">430</integer>
82 <integer name="SWITCH_BUTTON_X_X_FOLDABLE">740</integer> 82 <integer name="BUTTON_X_X_FOLDABLE">740</integer>
83 <integer name="SWITCH_BUTTON_X_Y_FOLDABLE">350</integer> 83 <integer name="BUTTON_X_Y_FOLDABLE">350</integer>
84 <integer name="SWITCH_BUTTON_Y_X_FOLDABLE">640</integer> 84 <integer name="BUTTON_Y_X_FOLDABLE">640</integer>
85 <integer name="SWITCH_BUTTON_Y_Y_FOLDABLE">390</integer> 85 <integer name="BUTTON_Y_Y_FOLDABLE">390</integer>
86 <integer name="SWITCH_STICK_L_X_FOLDABLE">180</integer> 86 <integer name="BUTTON_PLUS_X_FOLDABLE">560</integer>
87 <integer name="SWITCH_STICK_L_Y_FOLDABLE">250</integer> 87 <integer name="BUTTON_PLUS_Y_FOLDABLE">470</integer>
88 <integer name="SWITCH_STICK_R_X_FOLDABLE">820</integer> 88 <integer name="BUTTON_MINUS_X_FOLDABLE">440</integer>
89 <integer name="SWITCH_STICK_R_Y_FOLDABLE">250</integer> 89 <integer name="BUTTON_MINUS_Y_FOLDABLE">470</integer>
90 <integer name="SWITCH_TRIGGER_L_X_FOLDABLE">140</integer> 90 <integer name="BUTTON_HOME_X_FOLDABLE">680</integer>
91 <integer name="SWITCH_TRIGGER_L_Y_FOLDABLE">130</integer> 91 <integer name="BUTTON_HOME_Y_FOLDABLE">470</integer>
92 <integer name="SWITCH_TRIGGER_R_X_FOLDABLE">860</integer> 92 <integer name="BUTTON_CAPTURE_X_FOLDABLE">320</integer>
93 <integer name="SWITCH_TRIGGER_R_Y_FOLDABLE">130</integer> 93 <integer name="BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
94 <integer name="SWITCH_TRIGGER_ZL_X_FOLDABLE">140</integer> 94 <integer name="BUTTON_L_X_FOLDABLE">140</integer>
95 <integer name="SWITCH_TRIGGER_ZL_Y_FOLDABLE">70</integer> 95 <integer name="BUTTON_L_Y_FOLDABLE">130</integer>
96 <integer name="SWITCH_TRIGGER_ZR_X_FOLDABLE">860</integer> 96 <integer name="BUTTON_R_X_FOLDABLE">860</integer>
97 <integer name="SWITCH_TRIGGER_ZR_Y_FOLDABLE">70</integer> 97 <integer name="BUTTON_R_Y_FOLDABLE">130</integer>
98 <integer name="SWITCH_BUTTON_MINUS_X_FOLDABLE">440</integer> 98 <integer name="BUTTON_ZL_X_FOLDABLE">140</integer>
99 <integer name="SWITCH_BUTTON_MINUS_Y_FOLDABLE">470</integer> 99 <integer name="BUTTON_ZL_Y_FOLDABLE">70</integer>
100 <integer name="SWITCH_BUTTON_PLUS_X_FOLDABLE">560</integer> 100 <integer name="BUTTON_ZR_X_FOLDABLE">860</integer>
101 <integer name="SWITCH_BUTTON_PLUS_Y_FOLDABLE">470</integer> 101 <integer name="BUTTON_ZR_Y_FOLDABLE">70</integer>
102 <integer name="SWITCH_BUTTON_HOME_X_FOLDABLE">680</integer> 102 <integer name="BUTTON_STICK_L_X_FOLDABLE">550</integer>
103 <integer name="SWITCH_BUTTON_HOME_Y_FOLDABLE">470</integer> 103 <integer name="BUTTON_STICK_L_Y_FOLDABLE">210</integer>
104 <integer name="SWITCH_BUTTON_CAPTURE_X_FOLDABLE">320</integer> 104 <integer name="BUTTON_STICK_R_X_FOLDABLE">550</integer>
105 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> 105 <integer name="BUTTON_STICK_R_Y_FOLDABLE">280</integer>
106 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> 106 <integer name="STICK_L_X_FOLDABLE">180</integer>
107 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> 107 <integer name="STICK_L_Y_FOLDABLE">250</integer>
108 <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer> 108 <integer name="STICK_R_X_FOLDABLE">820</integer>
109 <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer> 109 <integer name="STICK_R_Y_FOLDABLE">250</integer>
110 <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer> 110 <integer name="COMBINED_DPAD_X_FOLDABLE">240</integer>
111 <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer> 111 <integer name="COMBINED_DPAD_Y_FOLDABLE">390</integer>
112 112
113</resources> 113</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 0b80b04a4..83aa1b781 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -225,6 +225,8 @@
225 <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> 225 <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
226 <string name="use_disk_shader_cache">Disk shader cache</string> 226 <string name="use_disk_shader_cache">Disk shader cache</string>
227 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string> 227 <string name="use_disk_shader_cache_description">Reduces stuttering by locally storing and loading generated shaders.</string>
228 <string name="anisotropic_filtering">Anisotropic filtering</string>
229 <string name="anisotropic_filtering_description">Improves the quality of textures when viewed at oblique angles</string>
228 230
229 <!-- Debug settings strings --> 231 <!-- Debug settings strings -->
230 <string name="cpu">CPU</string> 232 <string name="cpu">CPU</string>
@@ -364,6 +366,7 @@
364 <string name="emulation_pause">Pause emulation</string> 366 <string name="emulation_pause">Pause emulation</string>
365 <string name="emulation_unpause">Unpause emulation</string> 367 <string name="emulation_unpause">Unpause emulation</string>
366 <string name="emulation_input_overlay">Overlay options</string> 368 <string name="emulation_input_overlay">Overlay options</string>
369 <string name="touchscreen">Touchscreen</string>
367 370
368 <string name="load_settings">Loading settings…</string> 371 <string name="load_settings">Loading settings…</string>
369 372
@@ -460,9 +463,13 @@
460 <string name="anti_aliasing_smaa">SMAA</string> 463 <string name="anti_aliasing_smaa">SMAA</string>
461 464
462 <!-- Screen Layouts --> 465 <!-- Screen Layouts -->
466 <string name="screen_layout_auto">Auto</string>
467 <string name="screen_layout_sensor_landscape">Sensor landscape</string>
463 <string name="screen_layout_landscape">Landscape</string> 468 <string name="screen_layout_landscape">Landscape</string>
469 <string name="screen_layout_reverse_landscape">Reverse landscape</string>
470 <string name="screen_layout_sensor_portrait">Sensor portrait</string>
464 <string name="screen_layout_portrait">Portrait</string> 471 <string name="screen_layout_portrait">Portrait</string>
465 <string name="screen_layout_auto">Auto</string> 472 <string name="screen_layout_reverse_portrait">Reverse portrait</string>
466 473
467 <!-- Aspect Ratios --> 474 <!-- Aspect Ratios -->
468 <string name="ratio_default">Default (16:9)</string> 475 <string name="ratio_default">Default (16:9)</string>
@@ -506,6 +513,12 @@
506 <string name="oboe">oboe</string> 513 <string name="oboe">oboe</string>
507 <string name="cubeb">cubeb</string> 514 <string name="cubeb">cubeb</string>
508 515
516 <!-- Anisotropic filtering options -->
517 <string name="multiplier_two">2x</string>
518 <string name="multiplier_four">4x</string>
519 <string name="multiplier_eight">8x</string>
520 <string name="multiplier_sixteen">16x</string>
521
509 <!-- Black backgrounds theme --> 522 <!-- Black backgrounds theme -->
510 <string name="use_black_backgrounds">Black backgrounds</string> 523 <string name="use_black_backgrounds">Black backgrounds</string>
511 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string> 524 <string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
diff --git a/src/android/build.gradle.kts b/src/android/build.gradle.kts
index 51e559321..b77906ed6 100644
--- a/src/android/build.gradle.kts
+++ b/src/android/build.gradle.kts
@@ -5,7 +5,7 @@
5plugins { 5plugins {
6 id("com.android.application") version "8.1.2" apply false 6 id("com.android.application") version "8.1.2" apply false
7 id("com.android.library") version "8.1.2" apply false 7 id("com.android.library") version "8.1.2" apply false
8 id("org.jetbrains.kotlin.android") version "1.8.21" apply false 8 id("org.jetbrains.kotlin.android") version "1.9.20" apply false
9} 9}
10 10
11tasks.register("clean").configure { 11tasks.register("clean").configure {
diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp
index c41d9d1ea..ee42ae529 100644
--- a/src/audio_core/device/device_session.cpp
+++ b/src/audio_core/device/device_session.cpp
@@ -18,9 +18,7 @@ constexpr auto INCREMENT_TIME{5ms};
18DeviceSession::DeviceSession(Core::System& system_) 18DeviceSession::DeviceSession(Core::System& system_)
19 : system{system_}, thread_event{Core::Timing::CreateEvent( 19 : system{system_}, thread_event{Core::Timing::CreateEvent(
20 "AudioOutSampleTick", 20 "AudioOutSampleTick",
21 [this](std::uintptr_t, s64 time, std::chrono::nanoseconds) { 21 [this](s64 time, std::chrono::nanoseconds) { return ThreadFunc(); })} {}
22 return ThreadFunc();
23 })} {}
24 22
25DeviceSession::~DeviceSession() { 23DeviceSession::~DeviceSession() {
26 Finalize(); 24 Finalize();
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index b58a7073f..8c57d47c6 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -64,6 +64,8 @@ add_library(common STATIC
64 fs/path_util.cpp 64 fs/path_util.cpp
65 fs/path_util.h 65 fs/path_util.h
66 hash.h 66 hash.h
67 heap_tracker.cpp
68 heap_tracker.h
67 hex_util.cpp 69 hex_util.cpp
68 hex_util.h 70 hex_util.h
69 host_memory.cpp 71 host_memory.cpp
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index 6026b7dc2..e2c2cade3 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -3,16 +3,19 @@
3 3
4#include "common/assert.h" 4#include "common/assert.h"
5#include "common/common_funcs.h" 5#include "common/common_funcs.h"
6#include "common/logging/backend.h"
6 7
7#include "common/settings.h" 8#include "common/settings.h"
8 9
9void assert_fail_impl() { 10void assert_fail_impl() {
10 if (Settings::values.use_debug_asserts) { 11 if (Settings::values.use_debug_asserts) {
12 Common::Log::Stop();
11 Crash(); 13 Crash();
12 } 14 }
13} 15}
14 16
15[[noreturn]] void unreachable_impl() { 17[[noreturn]] void unreachable_impl() {
18 Common::Log::Stop();
16 Crash(); 19 Crash();
17 throw std::runtime_error("Unreachable code"); 20 throw std::runtime_error("Unreachable code");
18} 21}
diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp
new file mode 100644
index 000000000..683208795
--- /dev/null
+++ b/src/common/heap_tracker.cpp
@@ -0,0 +1,281 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fstream>
5#include <vector>
6
7#include "common/heap_tracker.h"
8#include "common/logging/log.h"
9
10namespace Common {
11
12namespace {
13
14s64 GetMaxPermissibleResidentMapCount() {
15 // Default value.
16 s64 value = 65530;
17
18 // Try to read how many mappings we can make.
19 std::ifstream s("/proc/sys/vm/max_map_count");
20 s >> value;
21
22 // Print, for debug.
23 LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
24
25 // Allow 20000 maps for other code and to account for split inaccuracy.
26 return std::max<s64>(value - 20000, 0);
27}
28
29} // namespace
30
31HeapTracker::HeapTracker(Common::HostMemory& buffer)
32 : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
33HeapTracker::~HeapTracker() = default;
34
35void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
36 MemoryPermission perm, bool is_separate_heap) {
37 // When mapping other memory, map pages immediately.
38 if (!is_separate_heap) {
39 m_buffer.Map(virtual_offset, host_offset, length, perm, false);
40 return;
41 }
42
43 {
44 // We are mapping part of a separate heap.
45 std::scoped_lock lk{m_lock};
46
47 auto* const map = new SeparateHeapMap{
48 .vaddr = virtual_offset,
49 .paddr = host_offset,
50 .size = length,
51 .tick = m_tick++,
52 .perm = perm,
53 .is_resident = false,
54 };
55
56 // Insert into mappings.
57 m_map_count++;
58 m_mappings.insert(*map);
59 }
60
61 // Finally, map.
62 this->DeferredMapSeparateHeap(virtual_offset);
63}
64
65void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) {
66 // If this is a separate heap...
67 if (is_separate_heap) {
68 std::scoped_lock lk{m_lock};
69
70 const SeparateHeapMap key{
71 .vaddr = virtual_offset,
72 };
73
74 // Split at the boundaries of the region we are removing.
75 this->SplitHeapMapLocked(virtual_offset);
76 this->SplitHeapMapLocked(virtual_offset + size);
77
78 // Erase all mappings in range.
79 auto it = m_mappings.find(key);
80 while (it != m_mappings.end() && it->vaddr < virtual_offset + size) {
81 // Get underlying item.
82 auto* const item = std::addressof(*it);
83
84 // If resident, erase from resident map.
85 if (item->is_resident) {
86 ASSERT(--m_resident_map_count >= 0);
87 m_resident_mappings.erase(m_resident_mappings.iterator_to(*item));
88 }
89
90 // Erase from map.
91 ASSERT(--m_map_count >= 0);
92 it = m_mappings.erase(it);
93
94 // Free the item.
95 delete item;
96 }
97 }
98
99 // Unmap pages.
100 m_buffer.Unmap(virtual_offset, size, false);
101}
102
103void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) {
104 // Ensure no rebuild occurs while reprotecting.
105 std::shared_lock lk{m_rebuild_lock};
106
107 // Split at the boundaries of the region we are reprotecting.
108 this->SplitHeapMap(virtual_offset, size);
109
110 // Declare tracking variables.
111 const VAddr end = virtual_offset + size;
112 VAddr cur = virtual_offset;
113
114 while (cur < end) {
115 VAddr next = cur;
116 bool should_protect = false;
117
118 {
119 std::scoped_lock lk2{m_lock};
120
121 const SeparateHeapMap key{
122 .vaddr = next,
123 };
124
125 // Try to get the next mapping corresponding to this address.
126 const auto it = m_mappings.nfind(key);
127
128 if (it == m_mappings.end()) {
129 // There are no separate heap mappings remaining.
130 next = end;
131 should_protect = true;
132 } else if (it->vaddr == cur) {
133 // We are in range.
134 // Update permission bits.
135 it->perm = perm;
136
137 // Determine next address and whether we should protect.
138 next = cur + it->size;
139 should_protect = it->is_resident;
140 } else /* if (it->vaddr > cur) */ {
141 // We weren't in range, but there is a block coming up that will be.
142 next = it->vaddr;
143 should_protect = true;
144 }
145 }
146
147 // Clamp to end.
148 next = std::min(next, end);
149
150 // Reprotect, if we need to.
151 if (should_protect) {
152 m_buffer.Protect(cur, next - cur, perm);
153 }
154
155 // Advance.
156 cur = next;
157 }
158}
159
160bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) {
161 if (m_buffer.IsInVirtualRange(fault_address)) {
162 return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer());
163 }
164
165 return false;
166}
167
168bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
169 bool rebuild_required = false;
170
171 {
172 std::scoped_lock lk{m_lock};
173
174 // Check to ensure this was a non-resident separate heap mapping.
175 const auto it = this->GetNearestHeapMapLocked(virtual_offset);
176 if (it == m_mappings.end() || it->is_resident) {
177 return false;
178 }
179
180 // Update tick before possible rebuild.
181 it->tick = m_tick++;
182
183 // Check if we need to rebuild.
184 if (m_resident_map_count > m_max_resident_map_count) {
185 rebuild_required = true;
186 }
187
188 // Map the area.
189 m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false);
190
191 // This map is now resident.
192 it->is_resident = true;
193 m_resident_map_count++;
194 m_resident_mappings.insert(*it);
195 }
196
197 if (rebuild_required) {
198 // A rebuild was required, so perform it now.
199 this->RebuildSeparateHeapAddressSpace();
200 }
201
202 return true;
203}
204
205void HeapTracker::RebuildSeparateHeapAddressSpace() {
206 std::scoped_lock lk{m_rebuild_lock, m_lock};
207
208 ASSERT(!m_resident_mappings.empty());
209
210 // Dump half of the mappings.
211 //
212 // Despite being worse in theory, this has proven to be better in practice than more
213 // regularly dumping a smaller amount, because it significantly reduces average case
214 // lock contention.
215 const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
216 const size_t evict_count = m_resident_map_count - desired_count;
217 auto it = m_resident_mappings.begin();
218
219 for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) {
220 // Unmark and unmap.
221 it->is_resident = false;
222 m_buffer.Unmap(it->vaddr, it->size, false);
223
224 // Advance.
225 ASSERT(--m_resident_map_count >= 0);
226 it = m_resident_mappings.erase(it);
227 }
228}
229
230void HeapTracker::SplitHeapMap(VAddr offset, size_t size) {
231 std::scoped_lock lk{m_lock};
232
233 this->SplitHeapMapLocked(offset);
234 this->SplitHeapMapLocked(offset + size);
235}
236
237void HeapTracker::SplitHeapMapLocked(VAddr offset) {
238 const auto it = this->GetNearestHeapMapLocked(offset);
239 if (it == m_mappings.end() || it->vaddr == offset) {
240 // Not contained or no split required.
241 return;
242 }
243
244 // Cache the original values.
245 auto* const left = std::addressof(*it);
246 const size_t orig_size = left->size;
247
248 // Adjust the left map.
249 const size_t left_size = offset - left->vaddr;
250 left->size = left_size;
251
252 // Create the new right map.
253 auto* const right = new SeparateHeapMap{
254 .vaddr = left->vaddr + left_size,
255 .paddr = left->paddr + left_size,
256 .size = orig_size - left_size,
257 .tick = left->tick,
258 .perm = left->perm,
259 .is_resident = left->is_resident,
260 };
261
262 // Insert the new right map.
263 m_map_count++;
264 m_mappings.insert(*right);
265
266 // If resident, also insert into resident map.
267 if (right->is_resident) {
268 m_resident_map_count++;
269 m_resident_mappings.insert(*right);
270 }
271}
272
273HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) {
274 const SeparateHeapMap key{
275 .vaddr = offset,
276 };
277
278 return m_mappings.find(key);
279}
280
281} // namespace Common
diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h
new file mode 100644
index 000000000..ee5b0bf43
--- /dev/null
+++ b/src/common/heap_tracker.h
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <mutex>
8#include <set>
9#include <shared_mutex>
10
11#include "common/host_memory.h"
12#include "common/intrusive_red_black_tree.h"
13
14namespace Common {
15
16struct SeparateHeapMap {
17 Common::IntrusiveRedBlackTreeNode addr_node{};
18 Common::IntrusiveRedBlackTreeNode tick_node{};
19 VAddr vaddr{};
20 PAddr paddr{};
21 size_t size{};
22 size_t tick{};
23 MemoryPermission perm{};
24 bool is_resident{};
25};
26
27struct SeparateHeapMapAddrComparator {
28 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
29 if (lhs.vaddr < rhs.vaddr) {
30 return -1;
31 } else if (lhs.vaddr <= (rhs.vaddr + rhs.size - 1)) {
32 return 0;
33 } else {
34 return 1;
35 }
36 }
37};
38
39struct SeparateHeapMapTickComparator {
40 static constexpr int Compare(const SeparateHeapMap& lhs, const SeparateHeapMap& rhs) {
41 if (lhs.tick < rhs.tick) {
42 return -1;
43 } else if (lhs.tick > rhs.tick) {
44 return 1;
45 } else {
46 return SeparateHeapMapAddrComparator::Compare(lhs, rhs);
47 }
48 }
49};
50
51class HeapTracker {
52public:
53 explicit HeapTracker(Common::HostMemory& buffer);
54 ~HeapTracker();
55
56 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perm,
57 bool is_separate_heap);
58 void Unmap(size_t virtual_offset, size_t size, bool is_separate_heap);
59 void Protect(size_t virtual_offset, size_t length, MemoryPermission perm);
60 u8* VirtualBasePointer() {
61 return m_buffer.VirtualBasePointer();
62 }
63
64 bool DeferredMapSeparateHeap(u8* fault_address);
65 bool DeferredMapSeparateHeap(size_t virtual_offset);
66
67private:
68 using AddrTreeTraits =
69 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::addr_node>;
70 using AddrTree = AddrTreeTraits::TreeType<SeparateHeapMapAddrComparator>;
71
72 using TickTreeTraits =
73 Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&SeparateHeapMap::tick_node>;
74 using TickTree = TickTreeTraits::TreeType<SeparateHeapMapTickComparator>;
75
76 AddrTree m_mappings{};
77 TickTree m_resident_mappings{};
78
79private:
80 void SplitHeapMap(VAddr offset, size_t size);
81 void SplitHeapMapLocked(VAddr offset);
82
83 AddrTree::iterator GetNearestHeapMapLocked(VAddr offset);
84
85 void RebuildSeparateHeapAddressSpace();
86
87private:
88 Common::HostMemory& m_buffer;
89 const s64 m_max_resident_map_count;
90
91 std::shared_mutex m_rebuild_lock{};
92 std::mutex m_lock{};
93 s64 m_map_count{};
94 s64 m_resident_map_count{};
95 size_t m_tick{};
96};
97
98} // namespace Common
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index e540375b8..860c39e6a 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -679,7 +679,7 @@ HostMemory::HostMemory(HostMemory&&) noexcept = default;
679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default; 679HostMemory& HostMemory::operator=(HostMemory&&) noexcept = default;
680 680
681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length, 681void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
682 MemoryPermission perms) { 682 MemoryPermission perms, bool separate_heap) {
683 ASSERT(virtual_offset % PageAlignment == 0); 683 ASSERT(virtual_offset % PageAlignment == 0);
684 ASSERT(host_offset % PageAlignment == 0); 684 ASSERT(host_offset % PageAlignment == 0);
685 ASSERT(length % PageAlignment == 0); 685 ASSERT(length % PageAlignment == 0);
@@ -691,7 +691,7 @@ void HostMemory::Map(size_t virtual_offset, size_t host_offset, size_t length,
691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms); 691 impl->Map(virtual_offset + virtual_base_offset, host_offset, length, perms);
692} 692}
693 693
694void HostMemory::Unmap(size_t virtual_offset, size_t length) { 694void HostMemory::Unmap(size_t virtual_offset, size_t length, bool separate_heap) {
695 ASSERT(virtual_offset % PageAlignment == 0); 695 ASSERT(virtual_offset % PageAlignment == 0);
696 ASSERT(length % PageAlignment == 0); 696 ASSERT(length % PageAlignment == 0);
697 ASSERT(virtual_offset + length <= virtual_size); 697 ASSERT(virtual_offset + length <= virtual_size);
@@ -701,14 +701,16 @@ void HostMemory::Unmap(size_t virtual_offset, size_t length) {
701 impl->Unmap(virtual_offset + virtual_base_offset, length); 701 impl->Unmap(virtual_offset + virtual_base_offset, length);
702} 702}
703 703
704void HostMemory::Protect(size_t virtual_offset, size_t length, bool read, bool write, 704void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission perm) {
705 bool execute) {
706 ASSERT(virtual_offset % PageAlignment == 0); 705 ASSERT(virtual_offset % PageAlignment == 0);
707 ASSERT(length % PageAlignment == 0); 706 ASSERT(length % PageAlignment == 0);
708 ASSERT(virtual_offset + length <= virtual_size); 707 ASSERT(virtual_offset + length <= virtual_size);
709 if (length == 0 || !virtual_base || !impl) { 708 if (length == 0 || !virtual_base || !impl) {
710 return; 709 return;
711 } 710 }
711 const bool read = True(perm & MemoryPermission::Read);
712 const bool write = True(perm & MemoryPermission::Write);
713 const bool execute = True(perm & MemoryPermission::Execute);
712 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute); 714 impl->Protect(virtual_offset + virtual_base_offset, length, read, write, execute);
713} 715}
714 716
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index 747c5850c..72fbb05af 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -40,11 +40,12 @@ public:
40 HostMemory(HostMemory&& other) noexcept; 40 HostMemory(HostMemory&& other) noexcept;
41 HostMemory& operator=(HostMemory&& other) noexcept; 41 HostMemory& operator=(HostMemory&& other) noexcept;
42 42
43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms); 43 void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms,
44 bool separate_heap);
44 45
45 void Unmap(size_t virtual_offset, size_t length); 46 void Unmap(size_t virtual_offset, size_t length, bool separate_heap);
46 47
47 void Protect(size_t virtual_offset, size_t length, bool read, bool write, bool execute = false); 48 void Protect(size_t virtual_offset, size_t length, MemoryPermission perms);
48 49
49 void EnableDirectMappedAddress(); 50 void EnableDirectMappedAddress();
50 51
@@ -64,6 +65,10 @@ public:
64 return virtual_base; 65 return virtual_base;
65 } 66 }
66 67
68 bool IsInVirtualRange(void* address) const noexcept {
69 return address >= virtual_base && address < virtual_base + virtual_size;
70 }
71
67private: 72private:
68 size_t backing_size{}; 73 size_t backing_size{};
69 size_t virtual_size{}; 74 size_t virtual_size{};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d4f27197c..7a267f8c0 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -208,6 +208,10 @@ public:
208 instance->StartBackendThread(); 208 instance->StartBackendThread();
209 } 209 }
210 210
211 static void Stop() {
212 instance->StopBackendThread();
213 }
214
211 Impl(const Impl&) = delete; 215 Impl(const Impl&) = delete;
212 Impl& operator=(const Impl&) = delete; 216 Impl& operator=(const Impl&) = delete;
213 217
@@ -259,6 +263,15 @@ private:
259 }); 263 });
260 } 264 }
261 265
266 void StopBackendThread() {
267 backend_thread.request_stop();
268 if (backend_thread.joinable()) {
269 backend_thread.join();
270 }
271
272 ForEachBackend([](Backend& backend) { backend.Flush(); });
273 }
274
262 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr, 275 Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
263 const char* function, std::string&& message) const { 276 const char* function, std::string&& message) const {
264 using std::chrono::duration_cast; 277 using std::chrono::duration_cast;
@@ -313,6 +326,10 @@ void Start() {
313 Impl::Start(); 326 Impl::Start();
314} 327}
315 328
329void Stop() {
330 Impl::Stop();
331}
332
316void DisableLoggingInTests() { 333void DisableLoggingInTests() {
317 initialization_in_progress_suppress_logging = true; 334 initialization_in_progress_suppress_logging = true;
318} 335}
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 12e5e2498..2a9926e9e 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -14,6 +14,9 @@ void Initialize();
14 14
15void Start(); 15void Start();
16 16
17/// Explicitly stops the logger thread and flushes the buffers
18void Stop();
19
17void DisableLoggingInTests(); 20void DisableLoggingInTests();
18 21
19/** 22/**
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h
index 5c961b202..e7e9fdb38 100644
--- a/src/common/ring_buffer.h
+++ b/src/common/ring_buffer.h
@@ -103,7 +103,7 @@ private:
103 // Having them on the same cache-line would result in false-sharing between them. 103 // Having them on the same cache-line would result in false-sharing between them.
104 // TODO: Remove this ifdef whenever clang and GCC support 104 // TODO: Remove this ifdef whenever clang and GCC support
105 // std::hardware_destructive_interference_size. 105 // std::hardware_destructive_interference_size.
106#if defined(_MSC_VER) && _MSC_VER >= 1911 106#ifdef __cpp_lib_hardware_interference_size
107 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; 107 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
108 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; 108 alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
109#else 109#else
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index ea52bbfa6..07709d4e5 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -199,6 +199,8 @@ const char* TranslateCategory(Category category) {
199 case Category::CpuDebug: 199 case Category::CpuDebug:
200 case Category::CpuUnsafe: 200 case Category::CpuUnsafe:
201 return "Cpu"; 201 return "Cpu";
202 case Category::Overlay:
203 return "Overlay";
202 case Category::Renderer: 204 case Category::Renderer:
203 case Category::RendererAdvanced: 205 case Category::RendererAdvanced:
204 case Category::RendererDebug: 206 case Category::RendererDebug:
diff --git a/src/common/settings_common.h b/src/common/settings_common.h
index c82e17495..1a290ad77 100644
--- a/src/common/settings_common.h
+++ b/src/common/settings_common.h
@@ -18,6 +18,7 @@ enum class Category : u32 {
18 Cpu, 18 Cpu,
19 CpuDebug, 19 CpuDebug,
20 CpuUnsafe, 20 CpuUnsafe,
21 Overlay,
21 Renderer, 22 Renderer,
22 RendererAdvanced, 23 RendererAdvanced,
23 RendererDebug, 24 RendererDebug,
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 96ab39cb8..e2ec2164c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -549,21 +549,34 @@ add_library(core STATIC
549 hle/service/hid/xcd.cpp 549 hle/service/hid/xcd.cpp
550 hle/service/hid/xcd.h 550 hle/service/hid/xcd.h
551 hle/service/hid/errors.h 551 hle/service/hid/errors.h
552 hle/service/hid/controllers/npad/npad_data.cpp
553 hle/service/hid/controllers/npad/npad_data.h
554 hle/service/hid/controllers/npad/npad_resource.cpp
555 hle/service/hid/controllers/npad/npad_resource.h
552 hle/service/hid/controllers/types/debug_pad_types.h 556 hle/service/hid/controllers/types/debug_pad_types.h
553 hle/service/hid/controllers/types/keyboard_types.h 557 hle/service/hid/controllers/types/keyboard_types.h
554 hle/service/hid/controllers/types/mouse_types.h 558 hle/service/hid/controllers/types/mouse_types.h
555 hle/service/hid/controllers/types/npad_types.h 559 hle/service/hid/controllers/types/npad_types.h
560 hle/service/hid/controllers/types/shared_memory_format.h
556 hle/service/hid/controllers/types/touch_types.h 561 hle/service/hid/controllers/types/touch_types.h
557 hle/service/hid/controllers/applet_resource.cpp 562 hle/service/hid/controllers/applet_resource.cpp
558 hle/service/hid/controllers/applet_resource.h 563 hle/service/hid/controllers/applet_resource.h
564 hle/service/hid/controllers/capture_button.cpp
565 hle/service/hid/controllers/capture_button.h
559 hle/service/hid/controllers/console_six_axis.cpp 566 hle/service/hid/controllers/console_six_axis.cpp
560 hle/service/hid/controllers/console_six_axis.h 567 hle/service/hid/controllers/console_six_axis.h
561 hle/service/hid/controllers/controller_base.cpp 568 hle/service/hid/controllers/controller_base.cpp
562 hle/service/hid/controllers/controller_base.h 569 hle/service/hid/controllers/controller_base.h
570 hle/service/hid/controllers/debug_mouse.cpp
571 hle/service/hid/controllers/debug_mouse.h
563 hle/service/hid/controllers/debug_pad.cpp 572 hle/service/hid/controllers/debug_pad.cpp
564 hle/service/hid/controllers/debug_pad.h 573 hle/service/hid/controllers/debug_pad.h
574 hle/service/hid/controllers/digitizer.cpp
575 hle/service/hid/controllers/digitizer.h
565 hle/service/hid/controllers/gesture.cpp 576 hle/service/hid/controllers/gesture.cpp
566 hle/service/hid/controllers/gesture.h 577 hle/service/hid/controllers/gesture.h
578 hle/service/hid/controllers/home_button.cpp
579 hle/service/hid/controllers/home_button.h
567 hle/service/hid/controllers/keyboard.cpp 580 hle/service/hid/controllers/keyboard.cpp
568 hle/service/hid/controllers/keyboard.h 581 hle/service/hid/controllers/keyboard.h
569 hle/service/hid/controllers/mouse.cpp 582 hle/service/hid/controllers/mouse.cpp
@@ -574,15 +587,16 @@ add_library(core STATIC
574 hle/service/hid/controllers/palma.h 587 hle/service/hid/controllers/palma.h
575 hle/service/hid/controllers/seven_six_axis.cpp 588 hle/service/hid/controllers/seven_six_axis.cpp
576 hle/service/hid/controllers/seven_six_axis.h 589 hle/service/hid/controllers/seven_six_axis.h
577 hle/service/hid/controllers/shared_memory_format.h
578 hle/service/hid/controllers/shared_memory_holder.cpp 590 hle/service/hid/controllers/shared_memory_holder.cpp
579 hle/service/hid/controllers/shared_memory_holder.h 591 hle/service/hid/controllers/shared_memory_holder.h
580 hle/service/hid/controllers/six_axis.cpp 592 hle/service/hid/controllers/six_axis.cpp
581 hle/service/hid/controllers/six_axis.h 593 hle/service/hid/controllers/six_axis.h
582 hle/service/hid/controllers/stubbed.cpp 594 hle/service/hid/controllers/sleep_button.cpp
583 hle/service/hid/controllers/stubbed.h 595 hle/service/hid/controllers/sleep_button.h
584 hle/service/hid/controllers/touchscreen.cpp 596 hle/service/hid/controllers/touchscreen.cpp
585 hle/service/hid/controllers/touchscreen.h 597 hle/service/hid/controllers/touchscreen.h
598 hle/service/hid/controllers/unique_pad.cpp
599 hle/service/hid/controllers/unique_pad.h
586 hle/service/hid/hidbus/hidbus_base.cpp 600 hle/service/hid/hidbus/hidbus_base.cpp
587 hle/service/hid/hidbus/hidbus_base.h 601 hle/service/hid/hidbus/hidbus_base.h
588 hle/service/hid/hidbus/ringcon.cpp 602 hle/service/hid/hidbus/ringcon.cpp
@@ -978,6 +992,7 @@ endif()
978 992
979if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) 993if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
980 target_sources(core PRIVATE 994 target_sources(core PRIVATE
995 arm/dynarmic/arm_dynarmic.cpp
981 arm/dynarmic/arm_dynarmic.h 996 arm/dynarmic/arm_dynarmic.h
982 arm/dynarmic/arm_dynarmic_64.cpp 997 arm/dynarmic/arm_dynarmic_64.cpp
983 arm/dynarmic/arm_dynarmic_64.h 998 arm/dynarmic/arm_dynarmic_64.h
@@ -987,6 +1002,8 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
987 arm/dynarmic/dynarmic_cp15.h 1002 arm/dynarmic/dynarmic_cp15.h
988 arm/dynarmic/dynarmic_exclusive_monitor.cpp 1003 arm/dynarmic/dynarmic_exclusive_monitor.cpp
989 arm/dynarmic/dynarmic_exclusive_monitor.h 1004 arm/dynarmic/dynarmic_exclusive_monitor.h
1005 hle/service/jit/jit_code_memory.cpp
1006 hle/service/jit/jit_code_memory.h
990 hle/service/jit/jit_context.cpp 1007 hle/service/jit/jit_context.cpp
991 hle/service/jit/jit_context.h 1008 hle/service/jit/jit_context.h
992 hle/service/jit/jit.cpp 1009 hle/service/jit/jit.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 698c9c8ad..5dc7e5d59 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -9,7 +9,7 @@
9 9
10namespace Core { 10namespace Core {
11 11
12void ArmInterface::LogBacktrace(const Kernel::KProcess* process) const { 12void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
13 Kernel::Svc::ThreadContext ctx; 13 Kernel::Svc::ThreadContext ctx;
14 this->GetContext(ctx); 14 this->GetContext(ctx);
15 15
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 806c7c9e9..495963eef 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -95,7 +95,7 @@ public:
95 virtual void SignalInterrupt(Kernel::KThread* thread) = 0; 95 virtual void SignalInterrupt(Kernel::KThread* thread) = 0;
96 96
97 // Stack trace generation. 97 // Stack trace generation.
98 void LogBacktrace(const Kernel::KProcess* process) const; 98 void LogBacktrace(Kernel::KProcess* process) const;
99 99
100 // Debug functionality. 100 // Debug functionality.
101 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0; 101 virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
diff --git a/src/core/arm/debug.cpp b/src/core/arm/debug.cpp
index af1c34bc3..854509463 100644
--- a/src/core/arm/debug.cpp
+++ b/src/core/arm/debug.cpp
@@ -79,7 +79,7 @@ constexpr std::array<u64, 2> SegmentBases{
79 0x7100000000ULL, 79 0x7100000000ULL,
80}; 80};
81 81
82void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) { 82void SymbolicateBacktrace(Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
83 auto modules = FindModules(process); 83 auto modules = FindModules(process);
84 84
85 const bool is_64 = process->Is64Bit(); 85 const bool is_64 = process->Is64Bit();
@@ -118,7 +118,7 @@ void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<Backtrace
118 } 118 }
119} 119}
120 120
121std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process, 121std::vector<BacktraceEntry> GetAArch64Backtrace(Kernel::KProcess* process,
122 const Kernel::Svc::ThreadContext& ctx) { 122 const Kernel::Svc::ThreadContext& ctx) {
123 std::vector<BacktraceEntry> out; 123 std::vector<BacktraceEntry> out;
124 auto& memory = process->GetMemory(); 124 auto& memory = process->GetMemory();
@@ -144,7 +144,7 @@ std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
144 return out; 144 return out;
145} 145}
146 146
147std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process, 147std::vector<BacktraceEntry> GetAArch32Backtrace(Kernel::KProcess* process,
148 const Kernel::Svc::ThreadContext& ctx) { 148 const Kernel::Svc::ThreadContext& ctx) {
149 std::vector<BacktraceEntry> out; 149 std::vector<BacktraceEntry> out;
150 auto& memory = process->GetMemory(); 150 auto& memory = process->GetMemory();
@@ -173,7 +173,7 @@ std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
173} // namespace 173} // namespace
174 174
175std::optional<std::string> GetThreadName(const Kernel::KThread* thread) { 175std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
176 const auto* process = thread->GetOwnerProcess(); 176 auto* process = thread->GetOwnerProcess();
177 if (process->Is64Bit()) { 177 if (process->Is64Bit()) {
178 return GetNameFromThreadType64(process->GetMemory(), *thread); 178 return GetNameFromThreadType64(process->GetMemory(), *thread);
179 } else { 179 } else {
@@ -248,7 +248,7 @@ Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
248 return cur_addr - 1; 248 return cur_addr - 1;
249} 249}
250 250
251Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) { 251Loader::AppLoader::Modules FindModules(Kernel::KProcess* process) {
252 Loader::AppLoader::Modules modules; 252 Loader::AppLoader::Modules modules;
253 253
254 auto& page_table = process->GetPageTable(); 254 auto& page_table = process->GetPageTable();
@@ -312,7 +312,7 @@ Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
312 return modules; 312 return modules;
313} 313}
314 314
315Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) { 315Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process) {
316 // Do we have any loaded executable sections? 316 // Do we have any loaded executable sections?
317 auto modules = FindModules(process); 317 auto modules = FindModules(process);
318 318
@@ -337,7 +337,7 @@ void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 addres
337 } 337 }
338} 338}
339 339
340std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, 340std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
341 const Kernel::Svc::ThreadContext& ctx) { 341 const Kernel::Svc::ThreadContext& ctx) {
342 if (process->Is64Bit()) { 342 if (process->Is64Bit()) {
343 return GetAArch64Backtrace(process, ctx); 343 return GetAArch64Backtrace(process, ctx);
diff --git a/src/core/arm/debug.h b/src/core/arm/debug.h
index c542633db..3cd671365 100644
--- a/src/core/arm/debug.h
+++ b/src/core/arm/debug.h
@@ -14,9 +14,9 @@ std::optional<std::string> GetThreadName(const Kernel::KThread* thread);
14std::string_view GetThreadWaitReason(const Kernel::KThread* thread); 14std::string_view GetThreadWaitReason(const Kernel::KThread* thread);
15std::string GetThreadState(const Kernel::KThread* thread); 15std::string GetThreadState(const Kernel::KThread* thread);
16 16
17Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process); 17Loader::AppLoader::Modules FindModules(Kernel::KProcess* process);
18Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base); 18Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process, Kernel::KProcessAddress base);
19Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process); 19Kernel::KProcessAddress FindMainModuleEntrypoint(Kernel::KProcess* process);
20 20
21void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size); 21void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size);
22 22
@@ -28,7 +28,7 @@ struct BacktraceEntry {
28 std::string name; 28 std::string name;
29}; 29};
30 30
31std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process, 31std::vector<BacktraceEntry> GetBacktraceFromContext(Kernel::KProcess* process,
32 const Kernel::Svc::ThreadContext& ctx); 32 const Kernel::Svc::ThreadContext& ctx);
33std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread); 33std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread);
34 34
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
new file mode 100644
index 000000000..e6e9fc45b
--- /dev/null
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#ifdef __linux__
5
6#include "common/signal_chain.h"
7
8#include "core/arm/dynarmic/arm_dynarmic.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/memory.h"
11
12namespace Core {
13
14namespace {
15
16thread_local Core::Memory::Memory* g_current_memory{};
17std::once_flag g_registered{};
18struct sigaction g_old_segv {};
19
20void HandleSigSegv(int sig, siginfo_t* info, void* ctx) {
21 if (g_current_memory && g_current_memory->InvalidateSeparateHeap(info->si_addr)) {
22 return;
23 }
24
25 return g_old_segv.sa_sigaction(sig, info, ctx);
26}
27
28} // namespace
29
30ScopedJitExecution::ScopedJitExecution(Kernel::KProcess* process) {
31 g_current_memory = std::addressof(process->GetMemory());
32}
33
34ScopedJitExecution::~ScopedJitExecution() {
35 g_current_memory = nullptr;
36}
37
38void ScopedJitExecution::RegisterHandler() {
39 std::call_once(g_registered, [] {
40 struct sigaction sa {};
41 sa.sa_sigaction = &HandleSigSegv;
42 sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
43 Common::SigAction(SIGSEGV, std::addressof(sa), std::addressof(g_old_segv));
44 });
45}
46
47} // namespace Core
48
49#endif
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index eef7c3116..53dd18815 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -26,4 +26,24 @@ constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
26 return static_cast<HaltReason>(hr); 26 return static_cast<HaltReason>(hr);
27} 27}
28 28
29#ifdef __linux__
30
31class ScopedJitExecution {
32public:
33 explicit ScopedJitExecution(Kernel::KProcess* process);
34 ~ScopedJitExecution();
35 static void RegisterHandler();
36};
37
38#else
39
40class ScopedJitExecution {
41public:
42 explicit ScopedJitExecution(Kernel::KProcess* process) {}
43 ~ScopedJitExecution() {}
44 static void RegisterHandler() {}
45};
46
47#endif
48
29} // namespace Core 49} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index f34865e26..36478f722 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
15 15
16class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 16class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
17public: 17public:
18 explicit DynarmicCallbacks32(ArmDynarmic32& parent, const Kernel::KProcess* process) 18 explicit DynarmicCallbacks32(ArmDynarmic32& parent, Kernel::KProcess* process)
19 : m_parent{parent}, m_memory(process->GetMemory()), 19 : m_parent{parent}, m_memory(process->GetMemory()),
20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, 20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
21 m_check_memory_access{m_debugger_enabled || 21 m_check_memory_access{m_debugger_enabled ||
@@ -169,7 +169,7 @@ public:
169 169
170 ArmDynarmic32& m_parent; 170 ArmDynarmic32& m_parent;
171 Core::Memory::Memory& m_memory; 171 Core::Memory::Memory& m_memory;
172 const Kernel::KProcess* m_process{}; 172 Kernel::KProcess* m_process{};
173 const bool m_debugger_enabled{}; 173 const bool m_debugger_enabled{};
174 const bool m_check_memory_access{}; 174 const bool m_check_memory_access{};
175 static constexpr u64 MinimumRunCycles = 10000U; 175 static constexpr u64 MinimumRunCycles = 10000U;
@@ -331,11 +331,15 @@ bool ArmDynarmic32::IsInThumbMode() const {
331} 331}
332 332
333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) { 333HaltReason ArmDynarmic32::RunThread(Kernel::KThread* thread) {
334 ScopedJitExecution sj(thread->GetOwnerProcess());
335
334 m_jit->ClearExclusiveState(); 336 m_jit->ClearExclusiveState();
335 return TranslateHaltReason(m_jit->Run()); 337 return TranslateHaltReason(m_jit->Run());
336} 338}
337 339
338HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) { 340HaltReason ArmDynarmic32::StepThread(Kernel::KThread* thread) {
341 ScopedJitExecution sj(thread->GetOwnerProcess());
342
339 m_jit->ClearExclusiveState(); 343 m_jit->ClearExclusiveState();
340 return TranslateHaltReason(m_jit->Step()); 344 return TranslateHaltReason(m_jit->Step());
341} 345}
@@ -370,13 +374,14 @@ void ArmDynarmic32::RewindBreakpointInstruction() {
370 this->SetContext(m_breakpoint_context); 374 this->SetContext(m_breakpoint_context);
371} 375}
372 376
373ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 377ArmDynarmic32::ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
374 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) 378 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
375 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, 379 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
376 m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)), 380 m_cb(std::make_unique<DynarmicCallbacks32>(*this, process)),
377 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} { 381 m_cp15(std::make_shared<DynarmicCP15>(*this)), m_core_index{core_index} {
378 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl(); 382 auto& page_table_impl = process->GetPageTable().GetBasePageTable().GetImpl();
379 m_jit = MakeJit(&page_table_impl); 383 m_jit = MakeJit(&page_table_impl);
384 ScopedJitExecution::RegisterHandler();
380} 385}
381 386
382ArmDynarmic32::~ArmDynarmic32() = default; 387ArmDynarmic32::~ArmDynarmic32() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 185ac7cbf..b580efe61 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -20,7 +20,7 @@ class System;
20 20
21class ArmDynarmic32 final : public ArmInterface { 21class ArmDynarmic32 final : public ArmInterface {
22public: 22public:
23 ArmDynarmic32(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 23 ArmDynarmic32(System& system, bool uses_wall_clock, Kernel::KProcess* process,
24 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); 24 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
25 ~ArmDynarmic32() override; 25 ~ArmDynarmic32() override;
26 26
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index dff14756e..c811c8ad5 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -15,7 +15,7 @@ using namespace Common::Literals;
15 15
16class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 16class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
17public: 17public:
18 explicit DynarmicCallbacks64(ArmDynarmic64& parent, const Kernel::KProcess* process) 18 explicit DynarmicCallbacks64(ArmDynarmic64& parent, Kernel::KProcess* process)
19 : m_parent{parent}, m_memory(process->GetMemory()), 19 : m_parent{parent}, m_memory(process->GetMemory()),
20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()}, 20 m_process(process), m_debugger_enabled{parent.m_system.DebuggerEnabled()},
21 m_check_memory_access{m_debugger_enabled || 21 m_check_memory_access{m_debugger_enabled ||
@@ -216,7 +216,7 @@ public:
216 Core::Memory::Memory& m_memory; 216 Core::Memory::Memory& m_memory;
217 u64 m_tpidrro_el0{}; 217 u64 m_tpidrro_el0{};
218 u64 m_tpidr_el0{}; 218 u64 m_tpidr_el0{};
219 const Kernel::KProcess* m_process{}; 219 Kernel::KProcess* m_process{};
220 const bool m_debugger_enabled{}; 220 const bool m_debugger_enabled{};
221 const bool m_check_memory_access{}; 221 const bool m_check_memory_access{};
222 static constexpr u64 MinimumRunCycles = 10000U; 222 static constexpr u64 MinimumRunCycles = 10000U;
@@ -362,11 +362,15 @@ std::shared_ptr<Dynarmic::A64::Jit> ArmDynarmic64::MakeJit(Common::PageTable* pa
362} 362}
363 363
364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) { 364HaltReason ArmDynarmic64::RunThread(Kernel::KThread* thread) {
365 ScopedJitExecution sj(thread->GetOwnerProcess());
366
365 m_jit->ClearExclusiveState(); 367 m_jit->ClearExclusiveState();
366 return TranslateHaltReason(m_jit->Run()); 368 return TranslateHaltReason(m_jit->Run());
367} 369}
368 370
369HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) { 371HaltReason ArmDynarmic64::StepThread(Kernel::KThread* thread) {
372 ScopedJitExecution sj(thread->GetOwnerProcess());
373
370 m_jit->ClearExclusiveState(); 374 m_jit->ClearExclusiveState();
371 return TranslateHaltReason(m_jit->Step()); 375 return TranslateHaltReason(m_jit->Step());
372} 376}
@@ -399,13 +403,14 @@ void ArmDynarmic64::RewindBreakpointInstruction() {
399 this->SetContext(m_breakpoint_context); 403 this->SetContext(m_breakpoint_context);
400} 404}
401 405
402ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 406ArmDynarmic64::ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
403 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index) 407 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index)
404 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor}, 408 : ArmInterface{uses_wall_clock}, m_system{system}, m_exclusive_monitor{exclusive_monitor},
405 m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} { 409 m_cb(std::make_unique<DynarmicCallbacks64>(*this, process)), m_core_index{core_index} {
406 auto& page_table = process->GetPageTable().GetBasePageTable(); 410 auto& page_table = process->GetPageTable().GetBasePageTable();
407 auto& page_table_impl = page_table.GetImpl(); 411 auto& page_table_impl = page_table.GetImpl();
408 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth()); 412 m_jit = MakeJit(&page_table_impl, page_table.GetAddressSpaceWidth());
413 ScopedJitExecution::RegisterHandler();
409} 414}
410 415
411ArmDynarmic64::~ArmDynarmic64() = default; 416ArmDynarmic64::~ArmDynarmic64() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 4f3dd026f..08cd982b3 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -25,7 +25,7 @@ class System;
25 25
26class ArmDynarmic64 final : public ArmInterface { 26class ArmDynarmic64 final : public ArmInterface {
27public: 27public:
28 ArmDynarmic64(System& system, bool uses_wall_clock, const Kernel::KProcess* process, 28 ArmDynarmic64(System& system, bool uses_wall_clock, Kernel::KProcess* process,
29 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index); 29 DynarmicExclusiveMonitor& exclusive_monitor, std::size_t core_index);
30 ~ArmDynarmic64() override; 30 ~ArmDynarmic64() override;
31 31
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp
index 1311e66a9..123b3da7e 100644
--- a/src/core/arm/nce/arm_nce.cpp
+++ b/src/core/arm/nce/arm_nce.cpp
@@ -39,7 +39,7 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
39} 39}
40 40
41using namespace Common::Literals; 41using namespace Common::Literals;
42constexpr u32 StackSize = 32_KiB; 42constexpr u32 StackSize = 128_KiB;
43 43
44} // namespace 44} // namespace
45 45
diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp
index 8e81c66a5..def888d15 100644
--- a/src/core/arm/nce/interpreter_visitor.cpp
+++ b/src/core/arm/nce/interpreter_visitor.cpp
@@ -5,8 +5,6 @@
5#include "common/bit_cast.h" 5#include "common/bit_cast.h"
6#include "core/arm/nce/interpreter_visitor.h" 6#include "core/arm/nce/interpreter_visitor.h"
7 7
8#include <dynarmic/frontend/A64/decoder/a64.h>
9
10namespace Core { 8namespace Core {
11 9
12template <u32 BitSize> 10template <u32 BitSize>
@@ -249,6 +247,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
249 return false; 247 return false;
250 } 248 }
251 249
250 // Size in bytes
252 const u64 size = 4 << opc.ZeroExtend(); 251 const u64 size = 4 << opc.ZeroExtend();
253 const u64 offset = imm19.SignExtend<u64>() << 2; 252 const u64 offset = imm19.SignExtend<u64>() << 2;
254 const u64 address = this->GetPc() + offset; 253 const u64 address = this->GetPc() + offset;
@@ -530,7 +529,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
530 } 529 }
531 case MemOp::Load: { 530 case MemOp::Load: {
532 u128 data{}; 531 u128 data{};
533 m_memory.ReadBlock(address, &data, datasize); 532 m_memory.ReadBlock(address, &data, datasize / 8);
534 this->SetVec(Vt, data); 533 this->SetVec(Vt, data);
535 break; 534 break;
536 } 535 }
diff --git a/src/core/arm/nce/visitor_base.h b/src/core/arm/nce/visitor_base.h
index 8fb032912..6a2be3d9b 100644
--- a/src/core/arm/nce/visitor_base.h
+++ b/src/core/arm/nce/visitor_base.h
@@ -4,9 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#pragma GCC diagnostic push
8#pragma GCC diagnostic ignored "-Wshadow"
9
7#include <dynarmic/frontend/A64/a64_types.h> 10#include <dynarmic/frontend/A64/a64_types.h>
11#include <dynarmic/frontend/A64/decoder/a64.h>
8#include <dynarmic/frontend/imm.h> 12#include <dynarmic/frontend/imm.h>
9 13
14#pragma GCC diagnostic pop
15
10namespace Core { 16namespace Core {
11 17
12class VisitorBase { 18class VisitorBase {
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b14f74976..66f444d39 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -28,7 +28,6 @@
28#include "core/file_sys/savedata_factory.h" 28#include "core/file_sys/savedata_factory.h"
29#include "core/file_sys/vfs_concat.h" 29#include "core/file_sys/vfs_concat.h"
30#include "core/file_sys/vfs_real.h" 30#include "core/file_sys/vfs_real.h"
31#include "core/gpu_dirty_memory_manager.h"
32#include "core/hid/hid_core.h" 31#include "core/hid/hid_core.h"
33#include "core/hle/kernel/k_memory_manager.h" 32#include "core/hle/kernel/k_memory_manager.h"
34#include "core/hle/kernel/k_process.h" 33#include "core/hle/kernel/k_process.h"
@@ -130,11 +129,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
130 129
131struct System::Impl { 130struct System::Impl {
132 explicit Impl(System& system) 131 explicit Impl(System& system)
133 : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{}, 132 : kernel{system}, fs_controller{system}, hid_core{}, room_network{}, cpu_manager{system},
134 cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{}, 133 reporter{system}, applet_manager{system}, profile_manager{}, time_manager{system} {}
135 time_manager{system}, gpu_dirty_memory_write_manager{} {
136 memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
137 }
138 134
139 void Initialize(System& system) { 135 void Initialize(System& system) {
140 device_memory = std::make_unique<Core::DeviceMemory>(); 136 device_memory = std::make_unique<Core::DeviceMemory>();
@@ -241,17 +237,17 @@ struct System::Impl {
241 debugger = std::make_unique<Debugger>(system, port); 237 debugger = std::make_unique<Debugger>(system, port);
242 } 238 }
243 239
244 SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) { 240 void InitializeKernel(System& system) {
245 LOG_DEBUG(Core, "initialized OK"); 241 LOG_DEBUG(Core, "initialized OK");
246 242
247 // Setting changes may require a full system reinitialization (e.g., disabling multicore). 243 // Setting changes may require a full system reinitialization (e.g., disabling multicore).
248 ReinitializeIfNecessary(system); 244 ReinitializeIfNecessary(system);
249 245
250 memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
251
252 kernel.Initialize(); 246 kernel.Initialize();
253 cpu_manager.Initialize(); 247 cpu_manager.Initialize();
248 }
254 249
250 SystemResultStatus SetupForApplicationProcess(System& system, Frontend::EmuWindow& emu_window) {
255 /// Reset all glue registrations 251 /// Reset all glue registrations
256 arp_manager.ResetAll(); 252 arp_manager.ResetAll();
257 253
@@ -300,17 +296,9 @@ struct System::Impl {
300 return SystemResultStatus::ErrorGetLoader; 296 return SystemResultStatus::ErrorGetLoader;
301 } 297 }
302 298
303 SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; 299 InitializeKernel(system);
304 if (init_result != SystemResultStatus::Success) {
305 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
306 static_cast<int>(init_result));
307 ShutdownMainProcess();
308 return init_result;
309 }
310
311 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
312 300
313 // Create the process. 301 // Create the application process.
314 auto main_process = Kernel::KProcess::Create(system.Kernel()); 302 auto main_process = Kernel::KProcess::Create(system.Kernel());
315 Kernel::KProcess::Register(system.Kernel(), main_process); 303 Kernel::KProcess::Register(system.Kernel(), main_process);
316 kernel.AppendNewProcess(main_process); 304 kernel.AppendNewProcess(main_process);
@@ -323,7 +311,18 @@ struct System::Impl {
323 return static_cast<SystemResultStatus>( 311 return static_cast<SystemResultStatus>(
324 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result)); 312 static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
325 } 313 }
314
315 // Set up the rest of the system.
316 SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
317 if (init_result != SystemResultStatus::Success) {
318 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
319 static_cast<int>(init_result));
320 ShutdownMainProcess();
321 return init_result;
322 }
323
326 AddGlueRegistrationForProcess(*app_loader, *main_process); 324 AddGlueRegistrationForProcess(*app_loader, *main_process);
325 telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
327 326
328 // Initialize cheat engine 327 // Initialize cheat engine
329 if (cheat_engine) { 328 if (cheat_engine) {
@@ -426,7 +425,6 @@ struct System::Impl {
426 cpu_manager.Shutdown(); 425 cpu_manager.Shutdown();
427 debugger.reset(); 426 debugger.reset();
428 kernel.Shutdown(); 427 kernel.Shutdown();
429 memory.Reset();
430 Network::RestartSocketOperations(); 428 Network::RestartSocketOperations();
431 429
432 if (auto room_member = room_network.GetRoomMember().lock()) { 430 if (auto room_member = room_network.GetRoomMember().lock()) {
@@ -507,7 +505,6 @@ struct System::Impl {
507 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core; 505 std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
508 std::unique_ptr<Core::DeviceMemory> device_memory; 506 std::unique_ptr<Core::DeviceMemory> device_memory;
509 std::unique_ptr<AudioCore::AudioCore> audio_core; 507 std::unique_ptr<AudioCore::AudioCore> audio_core;
510 Core::Memory::Memory memory;
511 Core::HID::HIDCore hid_core; 508 Core::HID::HIDCore hid_core;
512 Network::RoomNetwork room_network; 509 Network::RoomNetwork room_network;
513 510
@@ -567,9 +564,6 @@ struct System::Impl {
567 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 564 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
568 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{}; 565 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_cpu{};
569 566
570 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES>
571 gpu_dirty_memory_write_manager{};
572
573 std::deque<std::vector<u8>> user_channel; 567 std::deque<std::vector<u8>> user_channel;
574}; 568};
575 569
@@ -652,29 +646,12 @@ void System::PrepareReschedule(const u32 core_index) {
652 impl->kernel.PrepareReschedule(core_index); 646 impl->kernel.PrepareReschedule(core_index);
653} 647}
654 648
655Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() {
656 const std::size_t core = impl->kernel.GetCurrentHostThreadID();
657 return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
658 ? core
659 : Core::Hardware::NUM_CPU_CORES - 1];
660}
661
662/// Provides a constant reference to the current gou dirty memory manager.
663const Core::GPUDirtyMemoryManager& System::CurrentGPUDirtyMemoryManager() const {
664 const std::size_t core = impl->kernel.GetCurrentHostThreadID();
665 return impl->gpu_dirty_memory_write_manager[core < Core::Hardware::NUM_CPU_CORES
666 ? core
667 : Core::Hardware::NUM_CPU_CORES - 1];
668}
669
670size_t System::GetCurrentHostThreadID() const { 649size_t System::GetCurrentHostThreadID() const {
671 return impl->kernel.GetCurrentHostThreadID(); 650 return impl->kernel.GetCurrentHostThreadID();
672} 651}
673 652
674void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) { 653void System::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
675 for (auto& manager : impl->gpu_dirty_memory_write_manager) { 654 return this->ApplicationProcess()->GatherGPUDirtyMemory(callback);
676 manager.Gather(callback);
677 }
678} 655}
679 656
680PerfStatsResults System::GetAndResetPerfStats() { 657PerfStatsResults System::GetAndResetPerfStats() {
@@ -723,20 +700,12 @@ const Kernel::KProcess* System::ApplicationProcess() const {
723 return impl->kernel.ApplicationProcess(); 700 return impl->kernel.ApplicationProcess();
724} 701}
725 702
726ExclusiveMonitor& System::Monitor() {
727 return impl->kernel.GetExclusiveMonitor();
728}
729
730const ExclusiveMonitor& System::Monitor() const {
731 return impl->kernel.GetExclusiveMonitor();
732}
733
734Memory::Memory& System::ApplicationMemory() { 703Memory::Memory& System::ApplicationMemory() {
735 return impl->memory; 704 return impl->kernel.ApplicationProcess()->GetMemory();
736} 705}
737 706
738const Core::Memory::Memory& System::ApplicationMemory() const { 707const Core::Memory::Memory& System::ApplicationMemory() const {
739 return impl->memory; 708 return impl->kernel.ApplicationProcess()->GetMemory();
740} 709}
741 710
742Tegra::GPU& System::GPU() { 711Tegra::GPU& System::GPU() {
diff --git a/src/core/core.h b/src/core/core.h
index 473204db7..ba5add0dc 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -116,7 +116,6 @@ class CpuManager;
116class Debugger; 116class Debugger;
117class DeviceMemory; 117class DeviceMemory;
118class ExclusiveMonitor; 118class ExclusiveMonitor;
119class GPUDirtyMemoryManager;
120class PerfStats; 119class PerfStats;
121class Reporter; 120class Reporter;
122class SpeedLimiter; 121class SpeedLimiter;
@@ -225,12 +224,6 @@ public:
225 /// Prepare the core emulation for a reschedule 224 /// Prepare the core emulation for a reschedule
226 void PrepareReschedule(u32 core_index); 225 void PrepareReschedule(u32 core_index);
227 226
228 /// Provides a reference to the gou dirty memory manager.
229 [[nodiscard]] Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager();
230
231 /// Provides a constant reference to the current gou dirty memory manager.
232 [[nodiscard]] const Core::GPUDirtyMemoryManager& CurrentGPUDirtyMemoryManager() const;
233
234 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback); 227 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
235 228
236 [[nodiscard]] size_t GetCurrentHostThreadID() const; 229 [[nodiscard]] size_t GetCurrentHostThreadID() const;
@@ -250,12 +243,6 @@ public:
250 /// Gets a const reference to the underlying CPU manager 243 /// Gets a const reference to the underlying CPU manager
251 [[nodiscard]] const CpuManager& GetCpuManager() const; 244 [[nodiscard]] const CpuManager& GetCpuManager() const;
252 245
253 /// Gets a reference to the exclusive monitor
254 [[nodiscard]] ExclusiveMonitor& Monitor();
255
256 /// Gets a constant reference to the exclusive monitor
257 [[nodiscard]] const ExclusiveMonitor& Monitor() const;
258
259 /// Gets a mutable reference to the system memory instance. 246 /// Gets a mutable reference to the system memory instance.
260 [[nodiscard]] Core::Memory::Memory& ApplicationMemory(); 247 [[nodiscard]] Core::Memory::Memory& ApplicationMemory();
261 248
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index d6b5abc68..fc536413b 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -29,7 +29,6 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
29struct CoreTiming::Event { 29struct CoreTiming::Event {
30 s64 time; 30 s64 time;
31 u64 fifo_order; 31 u64 fifo_order;
32 std::uintptr_t user_data;
33 std::weak_ptr<EventType> type; 32 std::weak_ptr<EventType> type;
34 s64 reschedule_time; 33 s64 reschedule_time;
35 heap_t::handle_type handle{}; 34 heap_t::handle_type handle{};
@@ -67,17 +66,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
67 event_fifo_id = 0; 66 event_fifo_id = 0;
68 shutting_down = false; 67 shutting_down = false;
69 cpu_ticks = 0; 68 cpu_ticks = 0;
70 const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
71 -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
72 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
73 if (is_multicore) { 69 if (is_multicore) {
74 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this)); 70 timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this));
75 } 71 }
76} 72}
77 73
78void CoreTiming::ClearPendingEvents() { 74void CoreTiming::ClearPendingEvents() {
79 std::scoped_lock lock{basic_lock}; 75 std::scoped_lock lock{advance_lock, basic_lock};
80 event_queue.clear(); 76 event_queue.clear();
77 event.Set();
81} 78}
82 79
83void CoreTiming::Pause(bool is_paused) { 80void CoreTiming::Pause(bool is_paused) {
@@ -119,14 +116,12 @@ bool CoreTiming::HasPendingEvents() const {
119} 116}
120 117
121void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, 118void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
122 const std::shared_ptr<EventType>& event_type, 119 const std::shared_ptr<EventType>& event_type, bool absolute_time) {
123 std::uintptr_t user_data, bool absolute_time) {
124 { 120 {
125 std::scoped_lock scope{basic_lock}; 121 std::scoped_lock scope{basic_lock};
126 const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future}; 122 const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
127 123
128 auto h{event_queue.emplace( 124 auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, event_type, 0})};
129 Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
130 (*h).handle = h; 125 (*h).handle = h;
131 } 126 }
132 127
@@ -136,13 +131,13 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
136void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time, 131void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
137 std::chrono::nanoseconds resched_time, 132 std::chrono::nanoseconds resched_time,
138 const std::shared_ptr<EventType>& event_type, 133 const std::shared_ptr<EventType>& event_type,
139 std::uintptr_t user_data, bool absolute_time) { 134 bool absolute_time) {
140 { 135 {
141 std::scoped_lock scope{basic_lock}; 136 std::scoped_lock scope{basic_lock};
142 const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time}; 137 const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
143 138
144 auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type, 139 auto h{event_queue.emplace(
145 resched_time.count()})}; 140 Event{next_time.count(), event_fifo_id++, event_type, resched_time.count()})};
146 (*h).handle = h; 141 (*h).handle = h;
147 } 142 }
148 143
@@ -150,14 +145,14 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
150} 145}
151 146
152void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, 147void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
153 std::uintptr_t user_data, bool wait) { 148 UnscheduleEventType type) {
154 { 149 {
155 std::scoped_lock lk{basic_lock}; 150 std::scoped_lock lk{basic_lock};
156 151
157 std::vector<heap_t::handle_type> to_remove; 152 std::vector<heap_t::handle_type> to_remove;
158 for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) { 153 for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
159 const Event& e = *itr; 154 const Event& e = *itr;
160 if (e.type.lock().get() == event_type.get() && e.user_data == user_data) { 155 if (e.type.lock().get() == event_type.get()) {
161 to_remove.push_back(itr->handle); 156 to_remove.push_back(itr->handle);
162 } 157 }
163 } 158 }
@@ -165,10 +160,12 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
165 for (auto h : to_remove) { 160 for (auto h : to_remove) {
166 event_queue.erase(h); 161 event_queue.erase(h);
167 } 162 }
163
164 event_type->sequence_number++;
168 } 165 }
169 166
170 // Force any in-progress events to finish 167 // Force any in-progress events to finish
171 if (wait) { 168 if (type == UnscheduleEventType::Wait) {
172 std::scoped_lock lk{advance_lock}; 169 std::scoped_lock lk{advance_lock};
173 } 170 }
174} 171}
@@ -208,28 +205,31 @@ std::optional<s64> CoreTiming::Advance() {
208 const Event& evt = event_queue.top(); 205 const Event& evt = event_queue.top();
209 206
210 if (const auto event_type{evt.type.lock()}) { 207 if (const auto event_type{evt.type.lock()}) {
211 if (evt.reschedule_time == 0) { 208 const auto evt_time = evt.time;
212 const auto evt_user_data = evt.user_data; 209 const auto evt_sequence_num = event_type->sequence_number;
213 const auto evt_time = evt.time;
214 210
211 if (evt.reschedule_time == 0) {
215 event_queue.pop(); 212 event_queue.pop();
216 213
217 basic_lock.unlock(); 214 basic_lock.unlock();
218 215
219 event_type->callback( 216 event_type->callback(
220 evt_user_data, evt_time, 217 evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
221 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
222 218
223 basic_lock.lock(); 219 basic_lock.lock();
224 } else { 220 } else {
225 basic_lock.unlock(); 221 basic_lock.unlock();
226 222
227 const auto new_schedule_time{event_type->callback( 223 const auto new_schedule_time{event_type->callback(
228 evt.user_data, evt.time, 224 evt_time, std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time})};
229 std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
230 225
231 basic_lock.lock(); 226 basic_lock.lock();
232 227
228 if (evt_sequence_num != event_type->sequence_number) {
229 // Heap handle is invalidated after external modification.
230 continue;
231 }
232
233 const auto next_schedule_time{new_schedule_time.has_value() 233 const auto next_schedule_time{new_schedule_time.has_value()
234 ? new_schedule_time.value().count() 234 ? new_schedule_time.value().count()
235 : evt.reschedule_time}; 235 : evt.reschedule_time};
@@ -241,8 +241,8 @@ std::optional<s64> CoreTiming::Advance() {
241 next_time = pause_end_time + next_schedule_time; 241 next_time = pause_end_time + next_schedule_time;
242 } 242 }
243 243
244 event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data, 244 event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.type,
245 evt.type, next_schedule_time, evt.handle}); 245 next_schedule_time, evt.handle});
246 } 246 }
247 } 247 }
248 248
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 21548f0a9..7e4dff7f3 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -22,17 +22,25 @@ namespace Core::Timing {
22 22
23/// A callback that may be scheduled for a particular core timing event. 23/// A callback that may be scheduled for a particular core timing event.
24using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>( 24using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
25 std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>; 25 s64 time, std::chrono::nanoseconds ns_late)>;
26 26
27/// Contains the characteristics of a particular event. 27/// Contains the characteristics of a particular event.
28struct EventType { 28struct EventType {
29 explicit EventType(TimedCallback&& callback_, std::string&& name_) 29 explicit EventType(TimedCallback&& callback_, std::string&& name_)
30 : callback{std::move(callback_)}, name{std::move(name_)} {} 30 : callback{std::move(callback_)}, name{std::move(name_)}, sequence_number{0} {}
31 31
32 /// The event's callback function. 32 /// The event's callback function.
33 TimedCallback callback; 33 TimedCallback callback;
34 /// A pointer to the name of the event. 34 /// A pointer to the name of the event.
35 const std::string name; 35 const std::string name;
36 /// A monotonic sequence number, incremented when this event is
37 /// changed externally.
38 size_t sequence_number;
39};
40
41enum class UnscheduleEventType {
42 Wait,
43 NoWait,
36}; 44};
37 45
38/** 46/**
@@ -89,23 +97,17 @@ public:
89 97
90 /// Schedules an event in core timing 98 /// Schedules an event in core timing
91 void ScheduleEvent(std::chrono::nanoseconds ns_into_future, 99 void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
92 const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0, 100 const std::shared_ptr<EventType>& event_type, bool absolute_time = false);
93 bool absolute_time = false);
94 101
95 /// Schedules an event which will automatically re-schedule itself with the given time, until 102 /// Schedules an event which will automatically re-schedule itself with the given time, until
96 /// unscheduled 103 /// unscheduled
97 void ScheduleLoopingEvent(std::chrono::nanoseconds start_time, 104 void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
98 std::chrono::nanoseconds resched_time, 105 std::chrono::nanoseconds resched_time,
99 const std::shared_ptr<EventType>& event_type, 106 const std::shared_ptr<EventType>& event_type,
100 std::uintptr_t user_data = 0, bool absolute_time = false); 107 bool absolute_time = false);
101 108
102 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data, 109 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
103 bool wait = true); 110 UnscheduleEventType type = UnscheduleEventType::Wait);
104
105 void UnscheduleEventWithoutWait(const std::shared_ptr<EventType>& event_type,
106 std::uintptr_t user_data) {
107 UnscheduleEvent(event_type, user_data, false);
108 }
109 111
110 void AddTicks(u64 ticks_to_add); 112 void AddTicks(u64 ticks_to_add);
111 113
@@ -158,7 +160,6 @@ private:
158 heap_t event_queue; 160 heap_t event_queue;
159 u64 event_fifo_id = 0; 161 u64 event_fifo_id = 0;
160 162
161 std::shared_ptr<EventType> ev_lost;
162 Common::Event event{}; 163 Common::Event event{};
163 Common::Event pause_event{}; 164 Common::Event pause_event{};
164 mutable std::mutex basic_lock; 165 mutable std::mutex basic_lock;
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 7be1322cc..31033634c 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -73,6 +73,9 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
73 return nullptr; 73 return nullptr;
74 74
75 auto in_data = in->ReadAllBytes(); 75 auto in_data = in->ReadAllBytes();
76 if (in_data.size() == 0) {
77 return nullptr;
78 }
76 79
77 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); 80 std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
78 u64 offset = 5; // After header 81 u64 offset = 5; // After header
@@ -88,6 +91,10 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
88 else 91 else
89 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; 92 real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
90 93
94 if (real_offset > in_data.size()) {
95 return nullptr;
96 }
97
91 u16 data_size{}; 98 u16 data_size{};
92 if (ips->ReadObject(&data_size, offset) != sizeof(u16)) 99 if (ips->ReadObject(&data_size, offset) != sizeof(u16))
93 return nullptr; 100 return nullptr;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 763a44fee..539c7f7af 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -166,6 +166,10 @@ u32 ProgramMetadata::GetSystemResourceSize() const {
166 return npdm_header.system_resource_size; 166 return npdm_header.system_resource_size;
167} 167}
168 168
169PoolPartition ProgramMetadata::GetPoolPartition() const {
170 return acid_header.pool_partition;
171}
172
169const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { 173const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
170 return aci_kernel_capabilities; 174 return aci_kernel_capabilities;
171} 175}
@@ -201,7 +205,7 @@ void ProgramMetadata::Print() const {
201 // Begin ACID printing (potential perms, signed) 205 // Begin ACID printing (potential perms, signed)
202 LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data()); 206 LOG_DEBUG(Service_FS, "Magic: {:.4}", acid_header.magic.data());
203 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags); 207 LOG_DEBUG(Service_FS, "Flags: 0x{:02X}", acid_header.flags);
204 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.is_retail ? "YES" : "NO"); 208 LOG_DEBUG(Service_FS, " > Is Retail: {}", acid_header.production_flag ? "YES" : "NO");
205 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min); 209 LOG_DEBUG(Service_FS, "Title ID Min: 0x{:016X}", acid_header.title_id_min);
206 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max); 210 LOG_DEBUG(Service_FS, "Title ID Max: 0x{:016X}", acid_header.title_id_max);
207 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions); 211 LOG_DEBUG(Service_FS, "Filesystem Access: 0x{:016X}\n", acid_file_access.permissions);
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 76ee97d78..a53092b87 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -34,6 +34,13 @@ enum class ProgramFilePermission : u64 {
34 Everything = 1ULL << 63, 34 Everything = 1ULL << 63,
35}; 35};
36 36
37enum class PoolPartition : u32 {
38 Application = 0,
39 Applet = 1,
40 System = 2,
41 SystemNonSecure = 3,
42};
43
37/** 44/**
38 * Helper which implements an interface to parse Program Description Metadata (NPDM) 45 * Helper which implements an interface to parse Program Description Metadata (NPDM)
39 * Data can either be loaded from a file path or with data and an offset into it. 46 * Data can either be loaded from a file path or with data and an offset into it.
@@ -72,6 +79,7 @@ public:
72 u64 GetTitleID() const; 79 u64 GetTitleID() const;
73 u64 GetFilesystemPermissions() const; 80 u64 GetFilesystemPermissions() const;
74 u32 GetSystemResourceSize() const; 81 u32 GetSystemResourceSize() const;
82 PoolPartition GetPoolPartition() const;
75 const KernelCapabilityDescriptors& GetKernelCapabilities() const; 83 const KernelCapabilityDescriptors& GetKernelCapabilities() const;
76 const std::array<u8, 0x10>& GetName() const { 84 const std::array<u8, 0x10>& GetName() const {
77 return npdm_header.application_name; 85 return npdm_header.application_name;
@@ -116,8 +124,9 @@ private:
116 union { 124 union {
117 u32 flags; 125 u32 flags;
118 126
119 BitField<0, 1, u32> is_retail; 127 BitField<0, 1, u32> production_flag;
120 BitField<1, 31, u32> flags_unk; 128 BitField<1, 1, u32> unqualified_approval;
129 BitField<2, 4, PoolPartition> pool_partition;
121 }; 130 };
122 u64_le title_id_min; 131 u64_le title_id_min;
123 u64_le title_id_max; 132 u64_le title_id_max;
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 4bf285f36..a81ed6af0 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -267,6 +267,7 @@ enum class NpadStyleSet : u32 {
267 All = 0xFFFFFFFFU, 267 All = 0xFFFFFFFFU,
268}; 268};
269static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size"); 269static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
270DECLARE_ENUM_FLAG_OPERATORS(NpadStyleSet)
270 271
271// This is nn::hid::VibrationDevicePosition 272// This is nn::hid::VibrationDevicePosition
272enum class VibrationDevicePosition : u32 { 273enum class VibrationDevicePosition : u32 {
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 78d43d729..48889253d 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -4,6 +4,7 @@
4#include "core/arm/exclusive_monitor.h" 4#include "core/arm/exclusive_monitor.h"
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/kernel/k_address_arbiter.h" 6#include "core/hle/kernel/k_address_arbiter.h"
7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 9#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -26,9 +27,9 @@ bool ReadFromUser(KernelCore& kernel, s32* out, KProcessAddress address) {
26 return true; 27 return true;
27} 28}
28 29
29bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address, s32 value) { 30bool DecrementIfLessThan(KernelCore& kernel, s32* out, KProcessAddress address, s32 value) {
30 auto& monitor = system.Monitor(); 31 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
31 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 32 const auto current_core = kernel.CurrentPhysicalCoreIndex();
32 33
33 // NOTE: If scheduler lock is not held here, interrupt disable is required. 34 // NOTE: If scheduler lock is not held here, interrupt disable is required.
34 // KScopedInterruptDisable di; 35 // KScopedInterruptDisable di;
@@ -66,10 +67,10 @@ bool DecrementIfLessThan(Core::System& system, s32* out, KProcessAddress address
66 return true; 67 return true;
67} 68}
68 69
69bool UpdateIfEqual(Core::System& system, s32* out, KProcessAddress address, s32 value, 70bool UpdateIfEqual(KernelCore& kernel, s32* out, KProcessAddress address, s32 value,
70 s32 new_value) { 71 s32 new_value) {
71 auto& monitor = system.Monitor(); 72 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
72 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 73 const auto current_core = kernel.CurrentPhysicalCoreIndex();
73 74
74 // NOTE: If scheduler lock is not held here, interrupt disable is required. 75 // NOTE: If scheduler lock is not held here, interrupt disable is required.
75 // KScopedInterruptDisable di; 76 // KScopedInterruptDisable di;
@@ -159,7 +160,7 @@ Result KAddressArbiter::SignalAndIncrementIfEqual(uint64_t addr, s32 value, s32
159 160
160 // Check the userspace value. 161 // Check the userspace value.
161 s32 user_value{}; 162 s32 user_value{};
162 R_UNLESS(UpdateIfEqual(m_system, std::addressof(user_value), addr, value, value + 1), 163 R_UNLESS(UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, value + 1),
163 ResultInvalidCurrentMemory); 164 ResultInvalidCurrentMemory);
164 R_UNLESS(user_value == value, ResultInvalidState); 165 R_UNLESS(user_value == value, ResultInvalidState);
165 166
@@ -219,7 +220,7 @@ Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(uint64_t addr, s32
219 s32 user_value{}; 220 s32 user_value{};
220 bool succeeded{}; 221 bool succeeded{};
221 if (value != new_value) { 222 if (value != new_value) {
222 succeeded = UpdateIfEqual(m_system, std::addressof(user_value), addr, value, new_value); 223 succeeded = UpdateIfEqual(m_kernel, std::addressof(user_value), addr, value, new_value);
223 } else { 224 } else {
224 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); 225 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
225 } 226 }
@@ -262,7 +263,7 @@ Result KAddressArbiter::WaitIfLessThan(uint64_t addr, s32 value, bool decrement,
262 s32 user_value{}; 263 s32 user_value{};
263 bool succeeded{}; 264 bool succeeded{};
264 if (decrement) { 265 if (decrement) {
265 succeeded = DecrementIfLessThan(m_system, std::addressof(user_value), addr, value); 266 succeeded = DecrementIfLessThan(m_kernel, std::addressof(user_value), addr, value);
266 } else { 267 } else {
267 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr); 268 succeeded = ReadFromUser(m_kernel, std::addressof(user_value), addr);
268 } 269 }
diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp
index 636b3f993..7bea1a1c2 100644
--- a/src/core/hle/kernel/k_auto_object_container.cpp
+++ b/src/core/hle/kernel/k_auto_object_container.cpp
@@ -8,19 +8,22 @@
8namespace Kernel { 8namespace Kernel {
9 9
10void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) { 10void KAutoObjectWithListContainer::Register(KAutoObjectWithList* obj) {
11 KScopedLightLock lk(m_lock); 11 // KScopedInterruptDisable di;
12 KScopedSpinLock lk(m_lock);
12 13
13 m_object_list.insert_unique(*obj); 14 m_object_list.insert_unique(*obj);
14} 15}
15 16
16void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) { 17void KAutoObjectWithListContainer::Unregister(KAutoObjectWithList* obj) {
17 KScopedLightLock lk(m_lock); 18 // KScopedInterruptDisable di;
19 KScopedSpinLock lk(m_lock);
18 20
19 m_object_list.erase(*obj); 21 m_object_list.erase(*obj);
20} 22}
21 23
22size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) { 24size_t KAutoObjectWithListContainer::GetOwnedCount(KProcess* owner) {
23 KScopedLightLock lk(m_lock); 25 // KScopedInterruptDisable di;
26 KScopedSpinLock lk(m_lock);
24 27
25 return std::count_if(m_object_list.begin(), m_object_list.end(), 28 return std::count_if(m_object_list.begin(), m_object_list.end(),
26 [&](const auto& obj) { return obj.GetOwner() == owner; }); 29 [&](const auto& obj) { return obj.GetOwner() == owner; });
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
index badd75d2a..770743d21 100644
--- a/src/core/hle/kernel/k_auto_object_container.h
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -7,7 +7,7 @@
7 7
8#include "common/common_funcs.h" 8#include "common/common_funcs.h"
9#include "core/hle/kernel/k_auto_object.h" 9#include "core/hle/kernel/k_auto_object.h"
10#include "core/hle/kernel/k_light_lock.h" 10#include "core/hle/kernel/k_spin_lock.h"
11 11
12namespace Kernel { 12namespace Kernel {
13 13
@@ -21,32 +21,7 @@ public:
21 21
22 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>; 22 using ListType = boost::intrusive::rbtree<KAutoObjectWithList>;
23 23
24 class ListAccessor : public KScopedLightLock { 24 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(), m_object_list() {}
25 public:
26 explicit ListAccessor(KAutoObjectWithListContainer* container)
27 : KScopedLightLock(container->m_lock), m_list(container->m_object_list) {}
28 explicit ListAccessor(KAutoObjectWithListContainer& container)
29 : KScopedLightLock(container.m_lock), m_list(container.m_object_list) {}
30
31 typename ListType::iterator begin() const {
32 return m_list.begin();
33 }
34
35 typename ListType::iterator end() const {
36 return m_list.end();
37 }
38
39 typename ListType::iterator find(typename ListType::const_reference ref) const {
40 return m_list.find(ref);
41 }
42
43 private:
44 ListType& m_list;
45 };
46
47 friend class ListAccessor;
48
49 KAutoObjectWithListContainer(KernelCore& kernel) : m_lock(kernel), m_object_list() {}
50 25
51 void Initialize() {} 26 void Initialize() {}
52 void Finalize() {} 27 void Finalize() {}
@@ -56,7 +31,7 @@ public:
56 size_t GetOwnedCount(KProcess* owner); 31 size_t GetOwnedCount(KProcess* owner);
57 32
58private: 33private:
59 KLightLock m_lock; 34 KSpinLock m_lock;
60 ListType m_object_list; 35 ListType m_object_list;
61}; 36};
62 37
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
index 274fee493..d2288c30d 100644
--- a/src/core/hle/kernel/k_capabilities.cpp
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -185,6 +185,10 @@ Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
185 case RegionType::NoMapping: 185 case RegionType::NoMapping:
186 break; 186 break;
187 case RegionType::KernelTraceBuffer: 187 case RegionType::KernelTraceBuffer:
188 if constexpr (!IsKTraceEnabled) {
189 break;
190 }
191 [[fallthrough]];
188 case RegionType::OnMemoryBootImage: 192 case RegionType::OnMemoryBootImage:
189 case RegionType::DTB: 193 case RegionType::DTB:
190 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm)); 194 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
@@ -330,8 +334,6 @@ Result KCapabilities::SetCapabilities(std::span<const u32> caps, KProcessPageTab
330 334
331 // Map the range. 335 // Map the range.
332 R_TRY(this->MapRange_(cap, size_cap, page_table)); 336 R_TRY(this->MapRange_(cap, size_cap, page_table));
333 } else if (GetCapabilityType(cap) == CapabilityType::MapRegion && !IsKTraceEnabled) {
334 continue;
335 } else { 337 } else {
336 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table)); 338 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
337 } 339 }
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 11b1b977e..68cea978a 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,9 +58,8 @@ Result KClientPort::CreateSession(KClientSession** out) {
58 KSession* session{}; 58 KSession* session{};
59 59
60 // Reserve a new session from the resource limit. 60 // Reserve a new session from the resource limit.
61 //! FIXME: we are reserving this from the wrong resource limit! 61 KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
62 KScopedResourceReservation session_reservation( 62 LimitableResource::SessionCountMax);
63 m_kernel.ApplicationProcess()->GetResourceLimit(), LimitableResource::SessionCountMax);
64 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); 63 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
65 64
66 // Allocate a session normally. 65 // Allocate a session normally.
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 7633a51fb..94ea3527a 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -28,10 +28,10 @@ bool WriteToUser(KernelCore& kernel, KProcessAddress address, const u32* p) {
28 return true; 28 return true;
29} 29}
30 30
31bool UpdateLockAtomic(Core::System& system, u32* out, KProcessAddress address, u32 if_zero, 31bool UpdateLockAtomic(KernelCore& kernel, u32* out, KProcessAddress address, u32 if_zero,
32 u32 new_orr_mask) { 32 u32 new_orr_mask) {
33 auto& monitor = system.Monitor(); 33 auto& monitor = GetCurrentProcess(kernel).GetExclusiveMonitor();
34 const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); 34 const auto current_core = kernel.CurrentPhysicalCoreIndex();
35 35
36 u32 expected{}; 36 u32 expected{};
37 37
@@ -208,7 +208,7 @@ void KConditionVariable::SignalImpl(KThread* thread) {
208 // TODO(bunnei): We should call CanAccessAtomic(..) here. 208 // TODO(bunnei): We should call CanAccessAtomic(..) here.
209 can_access = true; 209 can_access = true;
210 if (can_access) [[likely]] { 210 if (can_access) [[likely]] {
211 UpdateLockAtomic(m_system, std::addressof(prev_tag), address, own_tag, 211 UpdateLockAtomic(m_kernel, std::addressof(prev_tag), address, own_tag,
212 Svc::HandleWaitMask); 212 Svc::HandleWaitMask);
213 } 213 }
214 } 214 }
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index d7660630c..1bf68e6b0 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -90,8 +90,7 @@ public:
90 // Handle pseudo-handles. 90 // Handle pseudo-handles.
91 if constexpr (std::derived_from<KProcess, T>) { 91 if constexpr (std::derived_from<KProcess, T>) {
92 if (handle == Svc::PseudoHandle::CurrentProcess) { 92 if (handle == Svc::PseudoHandle::CurrentProcess) {
93 //! FIXME: this is the wrong process! 93 auto* const cur_process = GetCurrentProcessPointer(m_kernel);
94 auto* const cur_process = m_kernel.ApplicationProcess();
95 ASSERT(cur_process != nullptr); 94 ASSERT(cur_process != nullptr);
96 return cur_process; 95 return cur_process;
97 } 96 }
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 8e2e40307..4e947dd6b 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -10,15 +10,15 @@ namespace Kernel {
10 10
11void KHardwareTimer::Initialize() { 11void KHardwareTimer::Initialize() {
12 // Create the timing callback to register with CoreTiming. 12 // Create the timing callback to register with CoreTiming.
13 m_event_type = Core::Timing::CreateEvent( 13 m_event_type = Core::Timing::CreateEvent("KHardwareTimer::Callback",
14 "KHardwareTimer::Callback", [](std::uintptr_t timer_handle, s64, std::chrono::nanoseconds) { 14 [this](s64, std::chrono::nanoseconds) {
15 reinterpret_cast<KHardwareTimer*>(timer_handle)->DoTask(); 15 this->DoTask();
16 return std::nullopt; 16 return std::nullopt;
17 }); 17 });
18} 18}
19 19
20void KHardwareTimer::Finalize() { 20void KHardwareTimer::Finalize() {
21 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type, reinterpret_cast<uintptr_t>(this)); 21 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type);
22 m_wakeup_time = std::numeric_limits<s64>::max(); 22 m_wakeup_time = std::numeric_limits<s64>::max();
23 m_event_type.reset(); 23 m_event_type.reset();
24} 24}
@@ -57,13 +57,12 @@ void KHardwareTimer::EnableInterrupt(s64 wakeup_time) {
57 57
58 m_wakeup_time = wakeup_time; 58 m_wakeup_time = wakeup_time;
59 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time}, 59 m_kernel.System().CoreTiming().ScheduleEvent(std::chrono::nanoseconds{m_wakeup_time},
60 m_event_type, reinterpret_cast<uintptr_t>(this), 60 m_event_type, true);
61 true);
62} 61}
63 62
64void KHardwareTimer::DisableInterrupt() { 63void KHardwareTimer::DisableInterrupt() {
65 m_kernel.System().CoreTiming().UnscheduleEventWithoutWait(m_event_type, 64 m_kernel.System().CoreTiming().UnscheduleEvent(m_event_type,
66 reinterpret_cast<uintptr_t>(this)); 65 Core::Timing::UnscheduleEventType::NoWait);
67 m_wakeup_time = std::numeric_limits<s64>::max(); 66 m_wakeup_time = std::numeric_limits<s64>::max();
68} 67}
69 68
diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp
index 423289145..8c1549559 100644
--- a/src/core/hle/kernel/k_page_table_base.cpp
+++ b/src/core/hle/kernel/k_page_table_base.cpp
@@ -434,7 +434,7 @@ Result KPageTableBase::InitializeForProcess(Svc::CreateProcessFlag as_type, bool
434void KPageTableBase::Finalize() { 434void KPageTableBase::Finalize() {
435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) { 435 auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
436 if (Settings::IsFastmemEnabled()) { 436 if (Settings::IsFastmemEnabled()) {
437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size); 437 m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size, false);
438 } 438 }
439 }; 439 };
440 440
@@ -5243,7 +5243,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5243 // Unmap. 5243 // Unmap.
5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, 5244 R_ASSERT(this->Operate(updater.GetPageList(), cur_address,
5245 cur_pages, 0, false, unmap_properties, 5245 cur_pages, 0, false, unmap_properties,
5246 OperationType::Unmap, true)); 5246 OperationType::UnmapPhysical, true));
5247 } 5247 }
5248 5248
5249 // Check if we're done. 5249 // Check if we're done.
@@ -5326,7 +5326,7 @@ Result KPageTableBase::MapPhysicalMemory(KProcessAddress address, size_t size) {
5326 // Map the papges. 5326 // Map the papges.
5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages, 5327 R_TRY(this->Operate(updater.GetPageList(), cur_address, map_pages,
5328 cur_pg, map_properties, 5328 cur_pg, map_properties,
5329 OperationType::MapFirstGroup, false)); 5329 OperationType::MapFirstGroupPhysical, false));
5330 } 5330 }
5331 } 5331 }
5332 5332
@@ -5480,7 +5480,7 @@ Result KPageTableBase::UnmapPhysicalMemory(KProcessAddress address, size_t size)
5480 5480
5481 // Unmap. 5481 // Unmap.
5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false, 5482 R_ASSERT(this->Operate(updater.GetPageList(), cur_address, cur_pages, 0, false,
5483 unmap_properties, OperationType::Unmap, false)); 5483 unmap_properties, OperationType::UnmapPhysical, false));
5484 } 5484 }
5485 5485
5486 // Check if we're done. 5486 // Check if we're done.
@@ -5655,7 +5655,10 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5655 // or free them to the page list, and so it goes unused (along with page properties). 5655 // or free them to the page list, and so it goes unused (along with page properties).
5656 5656
5657 switch (operation) { 5657 switch (operation) {
5658 case OperationType::Unmap: { 5658 case OperationType::Unmap:
5659 case OperationType::UnmapPhysical: {
5660 const bool separate_heap = operation == OperationType::UnmapPhysical;
5661
5659 // Ensure that any pages we track are closed on exit. 5662 // Ensure that any pages we track are closed on exit.
5660 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager()); 5663 KPageGroup pages_to_close(m_kernel, this->GetBlockInfoManager());
5661 SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); 5664 SCOPE_EXIT({ pages_to_close.CloseAndReset(); });
@@ -5664,7 +5667,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5664 this->MakePageGroup(pages_to_close, virt_addr, num_pages); 5667 this->MakePageGroup(pages_to_close, virt_addr, num_pages);
5665 5668
5666 // Unmap. 5669 // Unmap.
5667 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize); 5670 m_memory->UnmapRegion(*m_impl, virt_addr, num_pages * PageSize, separate_heap);
5668 5671
5669 R_SUCCEED(); 5672 R_SUCCEED();
5670 } 5673 }
@@ -5672,7 +5675,7 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5672 ASSERT(virt_addr != 0); 5675 ASSERT(virt_addr != 0);
5673 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize)); 5676 ASSERT(Common::IsAligned(GetInteger(virt_addr), PageSize));
5674 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr, 5677 m_memory->MapMemoryRegion(*m_impl, virt_addr, num_pages * PageSize, phys_addr,
5675 ConvertToMemoryPermission(properties.perm)); 5678 ConvertToMemoryPermission(properties.perm), false);
5676 5679
5677 // Open references to pages, if we should. 5680 // Open references to pages, if we should.
5678 if (this->IsHeapPhysicalAddress(phys_addr)) { 5681 if (this->IsHeapPhysicalAddress(phys_addr)) {
@@ -5711,16 +5714,19 @@ Result KPageTableBase::Operate(PageLinkedList* page_list, KProcessAddress virt_a
5711 5714
5712 switch (operation) { 5715 switch (operation) {
5713 case OperationType::MapGroup: 5716 case OperationType::MapGroup:
5714 case OperationType::MapFirstGroup: { 5717 case OperationType::MapFirstGroup:
5718 case OperationType::MapFirstGroupPhysical: {
5719 const bool separate_heap = operation == OperationType::MapFirstGroupPhysical;
5720
5715 // We want to maintain a new reference to every page in the group. 5721 // We want to maintain a new reference to every page in the group.
5716 KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup); 5722 KScopedPageGroup spg(page_group, operation == OperationType::MapGroup);
5717 5723
5718 for (const auto& node : page_group) { 5724 for (const auto& node : page_group) {
5719 const size_t size{node.GetNumPages() * PageSize}; 5725 const size_t size{node.GetNumPages() * PageSize};
5720 5726
5721 // Map the pages. 5727 // Map the pages.
5722 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(), 5728 m_memory->MapMemoryRegion(*m_impl, virt_addr, size, node.GetAddress(),
5723 ConvertToMemoryPermission(properties.perm)); 5729 ConvertToMemoryPermission(properties.perm), separate_heap);
5724 5730
5725 virt_addr += size; 5731 virt_addr += size;
5726 } 5732 }
diff --git a/src/core/hle/kernel/k_page_table_base.h b/src/core/hle/kernel/k_page_table_base.h
index 556d230b3..077cafc96 100644
--- a/src/core/hle/kernel/k_page_table_base.h
+++ b/src/core/hle/kernel/k_page_table_base.h
@@ -104,6 +104,9 @@ protected:
104 ChangePermissionsAndRefresh = 5, 104 ChangePermissionsAndRefresh = 5,
105 ChangePermissionsAndRefreshAndFlush = 6, 105 ChangePermissionsAndRefreshAndFlush = 6,
106 Separate = 7, 106 Separate = 7,
107
108 MapFirstGroupPhysical = 65000,
109 UnmapPhysical = 65001,
107 }; 110 };
108 111
109 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB; 112 static constexpr size_t MaxPhysicalMapAlignment = 1_GiB;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 3a2635e1f..068e71dff 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -306,12 +306,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params, const KPa
306 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 306 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
307 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 307 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
308 params.code_address, params.code_num_pages * PageSize, 308 params.code_address, params.code_num_pages * PageSize,
309 m_system_resource, res_limit, this->GetMemory(), 0)); 309 m_system_resource, res_limit, m_memory, 0));
310 } 310 }
311 ON_RESULT_FAILURE_2 { 311 ON_RESULT_FAILURE_2 {
312 m_page_table.Finalize(); 312 m_page_table.Finalize();
313 }; 313 };
314 314
315 // Ensure our memory is initialized.
316 m_memory.SetCurrentPageTable(*this);
317 m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
318
315 // Ensure we can insert the code region. 319 // Ensure we can insert the code region.
316 R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize, 320 R_UNLESS(m_page_table.CanContain(params.code_address, params.code_num_pages * PageSize,
317 KMemoryState::Code), 321 KMemoryState::Code),
@@ -399,12 +403,16 @@ Result KProcess::Initialize(const Svc::CreateProcessParameter& params,
399 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge); 403 False(params.flags & Svc::CreateProcessFlag::DisableDeviceAddressSpaceMerge);
400 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool, 404 R_TRY(m_page_table.Initialize(as_type, enable_aslr, enable_das_merge, !enable_aslr, pool,
401 params.code_address, code_size, m_system_resource, res_limit, 405 params.code_address, code_size, m_system_resource, res_limit,
402 this->GetMemory(), aslr_space_start)); 406 m_memory, aslr_space_start));
403 } 407 }
404 ON_RESULT_FAILURE_2 { 408 ON_RESULT_FAILURE_2 {
405 m_page_table.Finalize(); 409 m_page_table.Finalize();
406 }; 410 };
407 411
412 // Ensure our memory is initialized.
413 m_memory.SetCurrentPageTable(*this);
414 m_memory.SetGPUDirtyManagers(m_dirty_memory_managers);
415
408 // Ensure we can insert the code region. 416 // Ensure we can insert the code region.
409 R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code), 417 R_UNLESS(m_page_table.CanContain(params.code_address, code_size, KMemoryState::Code),
410 ResultInvalidMemoryRegion); 418 ResultInvalidMemoryRegion);
@@ -1094,8 +1102,7 @@ void KProcess::UnpinThread(KThread* thread) {
1094 1102
1095Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids, 1103Result KProcess::GetThreadList(s32* out_num_threads, KProcessAddress out_thread_ids,
1096 s32 max_out_count) { 1104 s32 max_out_count) {
1097 // TODO: use current memory reference 1105 auto& memory = this->GetMemory();
1098 auto& memory = m_kernel.System().ApplicationMemory();
1099 1106
1100 // Lock the list. 1107 // Lock the list.
1101 KScopedLightLock lk(m_list_lock); 1108 KScopedLightLock lk(m_list_lock);
@@ -1128,14 +1135,15 @@ void KProcess::Switch(KProcess* cur_process, KProcess* next_process) {}
1128KProcess::KProcess(KernelCore& kernel) 1135KProcess::KProcess(KernelCore& kernel)
1129 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel}, 1136 : KAutoObjectWithSlabHeapAndContainer(kernel), m_page_table{kernel}, m_state_lock{kernel},
1130 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()}, 1137 m_list_lock{kernel}, m_cond_var{kernel.System()}, m_address_arbiter{kernel.System()},
1131 m_handle_table{kernel} {} 1138 m_handle_table{kernel}, m_dirty_memory_managers{},
1139 m_exclusive_monitor{}, m_memory{kernel.System()} {}
1132KProcess::~KProcess() = default; 1140KProcess::~KProcess() = default;
1133 1141
1134Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size, 1142Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size,
1135 KProcessAddress aslr_space_start, bool is_hbl) { 1143 KProcessAddress aslr_space_start, bool is_hbl) {
1136 // Create a resource limit for the process. 1144 // Create a resource limit for the process.
1137 const auto physical_memory_size = 1145 const auto pool = static_cast<KMemoryManager::Pool>(metadata.GetPoolPartition());
1138 m_kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application); 1146 const auto physical_memory_size = m_kernel.MemoryManager().GetSize(pool);
1139 auto* res_limit = 1147 auto* res_limit =
1140 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size); 1148 Kernel::CreateResourceLimitForProcess(m_kernel.System(), physical_memory_size);
1141 1149
@@ -1146,8 +1154,10 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1146 Svc::CreateProcessFlag flag{}; 1154 Svc::CreateProcessFlag flag{};
1147 u64 code_address{}; 1155 u64 code_address{};
1148 1156
1149 // We are an application. 1157 // Determine if we are an application.
1150 flag |= Svc::CreateProcessFlag::IsApplication; 1158 if (pool == KMemoryManager::Pool::Application) {
1159 flag |= Svc::CreateProcessFlag::IsApplication;
1160 }
1151 1161
1152 // If we are 64-bit, create as such. 1162 // If we are 64-bit, create as such.
1153 if (metadata.Is64BitProgram()) { 1163 if (metadata.Is64BitProgram()) {
@@ -1196,8 +1206,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
1196 std::memcpy(params.name.data(), name.data(), sizeof(params.name)); 1206 std::memcpy(params.name.data(), name.data(), sizeof(params.name));
1197 1207
1198 // Initialize for application process. 1208 // Initialize for application process.
1199 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, 1209 R_TRY(this->Initialize(params, metadata.GetKernelCapabilities(), res_limit, pool,
1200 KMemoryManager::Pool::Application, aslr_space_start)); 1210 aslr_space_start));
1201 1211
1202 // Assign remaining properties. 1212 // Assign remaining properties.
1203 m_is_hbl = is_hbl; 1213 m_is_hbl = is_hbl;
@@ -1223,22 +1233,25 @@ void KProcess::LoadModule(CodeSet code_set, KProcessAddress base_addr) {
1223 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite); 1233 ReprotectSegment(code_set.DataSegment(), Svc::MemoryPermission::ReadWrite);
1224 1234
1225#ifdef HAS_NCE 1235#ifdef HAS_NCE
1226 if (Settings::IsNceEnabled()) { 1236 if (this->IsApplication() && Settings::IsNceEnabled()) {
1227 auto& buffer = m_kernel.System().DeviceMemory().buffer; 1237 auto& buffer = m_kernel.System().DeviceMemory().buffer;
1228 const auto& code = code_set.CodeSegment(); 1238 const auto& code = code_set.CodeSegment();
1229 const auto& patch = code_set.PatchSegment(); 1239 const auto& patch = code_set.PatchSegment();
1230 buffer.Protect(GetInteger(base_addr + code.addr), code.size, true, true, true); 1240 buffer.Protect(GetInteger(base_addr + code.addr), code.size,
1231 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size, true, true, true); 1241 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1242 buffer.Protect(GetInteger(base_addr + patch.addr), patch.size,
1243 Common::MemoryPermission::Read | Common::MemoryPermission::Execute);
1232 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None); 1244 ReprotectSegment(code_set.PatchSegment(), Svc::MemoryPermission::None);
1233 } 1245 }
1234#endif 1246#endif
1235} 1247}
1236 1248
1237void KProcess::InitializeInterfaces() { 1249void KProcess::InitializeInterfaces() {
1238 this->GetMemory().SetCurrentPageTable(*this); 1250 m_exclusive_monitor =
1251 Core::MakeExclusiveMonitor(this->GetMemory(), Core::Hardware::NUM_CPU_CORES);
1239 1252
1240#ifdef HAS_NCE 1253#ifdef HAS_NCE
1241 if (this->Is64Bit() && Settings::IsNceEnabled()) { 1254 if (this->IsApplication() && Settings::IsNceEnabled()) {
1242 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1255 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1243 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i); 1256 m_arm_interfaces[i] = std::make_unique<Core::ArmNce>(m_kernel.System(), true, i);
1244 } 1257 }
@@ -1248,13 +1261,13 @@ void KProcess::InitializeInterfaces() {
1248 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1261 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1249 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>( 1262 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic64>(
1250 m_kernel.System(), m_kernel.IsMulticore(), this, 1263 m_kernel.System(), m_kernel.IsMulticore(), this,
1251 static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); 1264 static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
1252 } 1265 }
1253 } else { 1266 } else {
1254 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 1267 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
1255 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>( 1268 m_arm_interfaces[i] = std::make_unique<Core::ArmDynarmic32>(
1256 m_kernel.System(), m_kernel.IsMulticore(), this, 1269 m_kernel.System(), m_kernel.IsMulticore(), this,
1257 static_cast<Core::DynarmicExclusiveMonitor&>(m_kernel.GetExclusiveMonitor()), i); 1270 static_cast<Core::DynarmicExclusiveMonitor&>(*m_exclusive_monitor), i);
1258 } 1271 }
1259 } 1272 }
1260} 1273}
@@ -1305,9 +1318,10 @@ bool KProcess::RemoveWatchpoint(KProcessAddress addr, u64 size, DebugWatchpointT
1305 return true; 1318 return true;
1306} 1319}
1307 1320
1308Core::Memory::Memory& KProcess::GetMemory() const { 1321void KProcess::GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback) {
1309 // TODO: per-process memory 1322 for (auto& manager : m_dirty_memory_managers) {
1310 return m_kernel.System().ApplicationMemory(); 1323 manager.Gather(callback);
1324 }
1311} 1325}
1312 1326
1313} // namespace Kernel 1327} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 4b114e39b..53c0e3316 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -7,6 +7,7 @@
7 7
8#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/gpu_dirty_memory_manager.h"
10#include "core/hle/kernel/code_set.h" 11#include "core/hle/kernel/code_set.h"
11#include "core/hle/kernel/k_address_arbiter.h" 12#include "core/hle/kernel/k_address_arbiter.h"
12#include "core/hle/kernel/k_capabilities.h" 13#include "core/hle/kernel/k_capabilities.h"
@@ -17,6 +18,7 @@
17#include "core/hle/kernel/k_system_resource.h" 18#include "core/hle/kernel/k_system_resource.h"
18#include "core/hle/kernel/k_thread.h" 19#include "core/hle/kernel/k_thread.h"
19#include "core/hle/kernel/k_thread_local_page.h" 20#include "core/hle/kernel/k_thread_local_page.h"
21#include "core/memory.h"
20 22
21namespace Kernel { 23namespace Kernel {
22 24
@@ -126,6 +128,9 @@ private:
126#ifdef HAS_NCE 128#ifdef HAS_NCE
127 std::unordered_map<u64, u64> m_post_handlers{}; 129 std::unordered_map<u64, u64> m_post_handlers{};
128#endif 130#endif
131 std::array<Core::GPUDirtyMemoryManager, Core::Hardware::NUM_CPU_CORES> m_dirty_memory_managers;
132 std::unique_ptr<Core::ExclusiveMonitor> m_exclusive_monitor;
133 Core::Memory::Memory m_memory;
129 134
130private: 135private:
131 Result StartTermination(); 136 Result StartTermination();
@@ -502,7 +507,15 @@ public:
502 507
503 void InitializeInterfaces(); 508 void InitializeInterfaces();
504 509
505 Core::Memory::Memory& GetMemory() const; 510 Core::Memory::Memory& GetMemory() {
511 return m_memory;
512 }
513
514 void GatherGPUDirtyMemory(std::function<void(VAddr, size_t)>& callback);
515
516 Core::ExclusiveMonitor& GetExclusiveMonitor() const {
517 return *m_exclusive_monitor;
518 }
506 519
507public: 520public:
508 // Overridden parent functions. 521 // Overridden parent functions.
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index e33a88e24..adaabdd6d 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -8,6 +8,7 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/scope_exit.h" 10#include "common/scope_exit.h"
11#include "common/scratch_buffer.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "core/core_timing.h" 13#include "core/core_timing.h"
13#include "core/hle/kernel/k_client_port.h" 14#include "core/hle/kernel/k_client_port.h"
@@ -29,12 +30,138 @@ namespace Kernel {
29 30
30namespace { 31namespace {
31 32
33constexpr inline size_t PointerTransferBufferAlignment = 0x10;
34constexpr inline size_t ReceiveListDataSize =
35 MessageBuffer::MessageHeader::ReceiveListCountType_CountMax *
36 MessageBuffer::ReceiveListEntry::GetDataSize() / sizeof(u32);
37
38using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
39
40class ReceiveList {
41public:
42 static constexpr int GetEntryCount(const MessageBuffer::MessageHeader& header) {
43 const auto count = header.GetReceiveListCount();
44 switch (count) {
45 case MessageBuffer::MessageHeader::ReceiveListCountType_None:
46 return 0;
47 case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer:
48 return 0;
49 case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer:
50 return 1;
51 default:
52 return count - MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset;
53 }
54 }
55
56 explicit ReceiveList(const u32* dst_msg, uint64_t dst_address,
57 KProcessPageTable& dst_page_table,
58 const MessageBuffer::MessageHeader& dst_header,
59 const MessageBuffer::SpecialHeader& dst_special_header, size_t msg_size,
60 size_t out_offset, s32 dst_recv_list_idx, bool is_tls) {
61 m_recv_list_count = dst_header.GetReceiveListCount();
62 m_msg_buffer_end = dst_address + sizeof(u32) * out_offset;
63 m_msg_buffer_space_end = dst_address + msg_size;
64
65 // NOTE: Nintendo calculates the receive list index here using the special header.
66 // We pre-calculate it in the caller, and pass it as a parameter.
67 (void)dst_special_header;
68
69 const u32* recv_list = dst_msg + dst_recv_list_idx;
70 const auto entry_count = GetEntryCount(dst_header);
71
72 if (is_tls) {
73 // Messages from TLS to TLS are contained within one page.
74 std::memcpy(m_data.data(), recv_list,
75 entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
76 } else {
77 // If any buffer is not from TLS, perform a normal read instead.
78 uint64_t cur_addr = dst_address + dst_recv_list_idx * sizeof(u32);
79 dst_page_table.GetMemory().ReadBlock(
80 cur_addr, m_data.data(),
81 entry_count * MessageBuffer::ReceiveListEntry::GetDataSize());
82 }
83 }
84
85 bool IsIndex() const {
86 return m_recv_list_count >
87 static_cast<s32>(MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset);
88 }
89
90 bool IsToMessageBuffer() const {
91 return m_recv_list_count ==
92 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer;
93 }
94
95 void GetBuffer(uint64_t& out, size_t size, int& key) const {
96 switch (m_recv_list_count) {
97 case MessageBuffer::MessageHeader::ReceiveListCountType_None: {
98 out = 0;
99 break;
100 }
101 case MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer: {
102 const uint64_t buf =
103 Common::AlignUp(m_msg_buffer_end + key, PointerTransferBufferAlignment);
104
105 if ((buf < buf + size) && (buf + size <= m_msg_buffer_space_end)) {
106 out = buf;
107 key = static_cast<int>(buf + size - m_msg_buffer_end);
108 } else {
109 out = 0;
110 }
111 break;
112 }
113 case MessageBuffer::MessageHeader::ReceiveListCountType_ToSingleBuffer: {
114 const MessageBuffer::ReceiveListEntry entry(m_data[0], m_data[1]);
115 const uint64_t buf =
116 Common::AlignUp(entry.GetAddress() + key, PointerTransferBufferAlignment);
117
118 const uint64_t entry_addr = entry.GetAddress();
119 const size_t entry_size = entry.GetSize();
120
121 if ((buf < buf + size) && (entry_addr < entry_addr + entry_size) &&
122 (buf + size <= entry_addr + entry_size)) {
123 out = buf;
124 key = static_cast<int>(buf + size - entry_addr);
125 } else {
126 out = 0;
127 }
128 break;
129 }
130 default: {
131 if (key < m_recv_list_count -
132 static_cast<s32>(
133 MessageBuffer::MessageHeader::ReceiveListCountType_CountOffset)) {
134 const MessageBuffer::ReceiveListEntry entry(m_data[2 * key + 0],
135 m_data[2 * key + 1]);
136
137 const uintptr_t entry_addr = entry.GetAddress();
138 const size_t entry_size = entry.GetSize();
139
140 if ((entry_addr < entry_addr + entry_size) && (entry_size >= size)) {
141 out = entry_addr;
142 }
143 } else {
144 out = 0;
145 }
146 break;
147 }
148 }
149 }
150
151private:
152 std::array<u32, ReceiveListDataSize> m_data;
153 s32 m_recv_list_count;
154 uint64_t m_msg_buffer_end;
155 uint64_t m_msg_buffer_space_end;
156};
157
32template <bool MoveHandleAllowed> 158template <bool MoveHandleAllowed>
33Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread, 159Result ProcessMessageSpecialData(s32& offset, KProcess& dst_process, KProcess& src_process,
34 MessageBuffer& dst_msg, const MessageBuffer& src_msg, 160 KThread& src_thread, const MessageBuffer& dst_msg,
35 MessageBuffer::SpecialHeader& src_special_header) { 161 const MessageBuffer& src_msg,
162 const MessageBuffer::SpecialHeader& src_special_header) {
36 // Copy the special header to the destination. 163 // Copy the special header to the destination.
37 s32 offset = dst_msg.Set(src_special_header); 164 offset = dst_msg.Set(src_special_header);
38 165
39 // Copy the process ID. 166 // Copy the process ID.
40 if (src_special_header.GetHasProcessId()) { 167 if (src_special_header.GetHasProcessId()) {
@@ -110,6 +237,102 @@ Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, K
110 R_RETURN(result); 237 R_RETURN(result);
111} 238}
112 239
240Result ProcessReceiveMessagePointerDescriptors(int& offset, int& pointer_key,
241 KProcessPageTable& dst_page_table,
242 KProcessPageTable& src_page_table,
243 const MessageBuffer& dst_msg,
244 const MessageBuffer& src_msg,
245 const ReceiveList& dst_recv_list, bool dst_user) {
246 // Get the offset at the start of processing.
247 const int cur_offset = offset;
248
249 // Get the pointer desc.
250 MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
251 offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
252
253 // Extract address/size.
254 const uint64_t src_pointer = src_desc.GetAddress();
255 const size_t recv_size = src_desc.GetSize();
256 uint64_t recv_pointer = 0;
257
258 // Process the buffer, if it has a size.
259 if (recv_size > 0) {
260 // If using indexing, set index.
261 if (dst_recv_list.IsIndex()) {
262 pointer_key = src_desc.GetIndex();
263 }
264
265 // Get the buffer.
266 dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
267 R_UNLESS(recv_pointer != 0, ResultOutOfResource);
268
269 // Perform the pointer data copy.
270 if (dst_user) {
271 R_TRY(src_page_table.CopyMemoryFromHeapToHeapWithoutCheckDestination(
272 dst_page_table, recv_pointer, recv_size, KMemoryState::FlagReferenceCounted,
273 KMemoryState::FlagReferenceCounted,
274 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
275 KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
276 src_pointer, KMemoryState::FlagLinearMapped, KMemoryState::FlagLinearMapped,
277 KMemoryPermission::UserRead, KMemoryAttribute::Uncached, KMemoryAttribute::None));
278 } else {
279 R_TRY(src_page_table.CopyMemoryFromLinearToUser(
280 recv_pointer, recv_size, src_pointer, KMemoryState::FlagLinearMapped,
281 KMemoryState::FlagLinearMapped, KMemoryPermission::UserRead,
282 KMemoryAttribute::Uncached, KMemoryAttribute::None));
283 }
284 }
285
286 // Set the output descriptor.
287 dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
288 recv_size, src_desc.GetIndex()));
289
290 R_SUCCEED();
291}
292
293constexpr Result GetMapAliasMemoryState(KMemoryState& out,
294 MessageBuffer::MapAliasDescriptor::Attribute attr) {
295 switch (attr) {
296 case MessageBuffer::MapAliasDescriptor::Attribute::Ipc:
297 out = KMemoryState::Ipc;
298 break;
299 case MessageBuffer::MapAliasDescriptor::Attribute::NonSecureIpc:
300 out = KMemoryState::NonSecureIpc;
301 break;
302 case MessageBuffer::MapAliasDescriptor::Attribute::NonDeviceIpc:
303 out = KMemoryState::NonDeviceIpc;
304 break;
305 default:
306 R_THROW(ResultInvalidCombination);
307 }
308
309 R_SUCCEED();
310}
311
312constexpr Result GetMapAliasTestStateAndAttributeMask(KMemoryState& out_state,
313 KMemoryAttribute& out_attr_mask,
314 KMemoryState state) {
315 switch (state) {
316 case KMemoryState::Ipc:
317 out_state = KMemoryState::FlagCanUseIpc;
318 out_attr_mask =
319 KMemoryAttribute::Uncached | KMemoryAttribute::DeviceShared | KMemoryAttribute::Locked;
320 break;
321 case KMemoryState::NonSecureIpc:
322 out_state = KMemoryState::FlagCanUseNonSecureIpc;
323 out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
324 break;
325 case KMemoryState::NonDeviceIpc:
326 out_state = KMemoryState::FlagCanUseNonDeviceIpc;
327 out_attr_mask = KMemoryAttribute::Uncached | KMemoryAttribute::Locked;
328 break;
329 default:
330 R_THROW(ResultInvalidCombination);
331 }
332
333 R_SUCCEED();
334}
335
113void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { 336void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) {
114 // Parse the message. 337 // Parse the message.
115 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size); 338 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
@@ -144,166 +367,855 @@ void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buff
144 } 367 }
145} 368}
146 369
147} // namespace 370Result CleanupServerHandles(KernelCore& kernel, uint64_t message, size_t buffer_size,
371 KPhysicalAddress message_paddr) {
372 // Server is assumed to be current thread.
373 KThread& thread = GetCurrentThread(kernel);
148 374
149using ThreadQueueImplForKServerSessionRequest = KThreadQueue; 375 // Get the linear message pointer.
376 u32* msg_ptr;
377 if (message) {
378 msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(message_paddr);
379 } else {
380 msg_ptr = GetCurrentMemory(kernel).GetPointer<u32>(thread.GetTlsAddress());
381 buffer_size = MessageBufferSize;
382 message = GetInteger(thread.GetTlsAddress());
383 }
150 384
151KServerSession::KServerSession(KernelCore& kernel) 385 // Parse the message.
152 : KSynchronizationObject{kernel}, m_lock{m_kernel} {} 386 const MessageBuffer msg(msg_ptr, buffer_size);
387 const MessageBuffer::MessageHeader header(msg);
388 const MessageBuffer::SpecialHeader special_header(msg, header);
153 389
154KServerSession::~KServerSession() = default; 390 // Check that the size is big enough.
391 R_UNLESS(MessageBuffer::GetMessageBufferSize(header, special_header) <= buffer_size,
392 ResultInvalidCombination);
393
394 // If there's a special header, there may be move handles we need to close.
395 if (header.GetHasSpecialHeader()) {
396 // Determine the offset to the start of handles.
397 auto offset = msg.GetSpecialDataIndex(header, special_header);
398 if (special_header.GetHasProcessId()) {
399 offset += static_cast<int>(sizeof(u64) / sizeof(u32));
400 }
401 if (auto copy_count = special_header.GetCopyHandleCount(); copy_count > 0) {
402 offset += static_cast<int>((sizeof(Svc::Handle) * copy_count) / sizeof(u32));
403 }
155 404
156void KServerSession::Destroy() { 405 // Get the handle table.
157 m_parent->OnServerClosed(); 406 auto& handle_table = thread.GetOwnerProcess()->GetHandleTable();
158 407
159 this->CleanupRequests(); 408 // Close the handles.
409 for (auto i = 0; i < special_header.GetMoveHandleCount(); ++i) {
410 handle_table.Remove(msg.GetHandle(offset));
411 offset += static_cast<int>(sizeof(Svc::Handle) / sizeof(u32));
412 }
413 }
160 414
161 m_parent->Close(); 415 R_SUCCEED();
162} 416}
163 417
164void KServerSession::OnClientClosed() { 418Result CleanupServerMap(KSessionRequest* request, KProcess* server_process) {
165 KScopedLightLock lk{m_lock}; 419 // If there's no server process, there's nothing to clean up.
420 R_SUCCEED_IF(server_process == nullptr);
166 421
167 // Handle any pending requests. 422 // Get the page table.
168 KSessionRequest* prev_request = nullptr; 423 auto& server_page_table = server_process->GetPageTable();
169 while (true) {
170 // Declare variables for processing the request.
171 KSessionRequest* request = nullptr;
172 KEvent* event = nullptr;
173 KThread* thread = nullptr;
174 bool cur_request = false;
175 bool terminate = false;
176 424
177 // Get the next request. 425 // Cleanup Send mappings.
178 { 426 for (size_t i = 0; i < request->GetSendCount(); ++i) {
179 KScopedSchedulerLock sl{m_kernel}; 427 R_TRY(server_page_table.CleanupForIpcServer(request->GetSendServerAddress(i),
428 request->GetSendSize(i),
429 request->GetSendMemoryState(i)));
430 }
180 431
181 if (m_current_request != nullptr && m_current_request != prev_request) { 432 // Cleanup Receive mappings.
182 // Set the request, open a reference as we process it. 433 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
183 request = m_current_request; 434 R_TRY(server_page_table.CleanupForIpcServer(request->GetReceiveServerAddress(i),
184 request->Open(); 435 request->GetReceiveSize(i),
185 cur_request = true; 436 request->GetReceiveMemoryState(i)));
437 }
186 438
187 // Get thread and event for the request. 439 // Cleanup Exchange mappings.
188 thread = request->GetThread(); 440 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
189 event = request->GetEvent(); 441 R_TRY(server_page_table.CleanupForIpcServer(request->GetExchangeServerAddress(i),
442 request->GetExchangeSize(i),
443 request->GetExchangeMemoryState(i)));
444 }
190 445
191 // If the thread is terminating, handle that. 446 R_SUCCEED();
192 if (thread->IsTerminationRequested()) { 447}
193 request->ClearThread();
194 request->ClearEvent();
195 terminate = true;
196 }
197 448
198 prev_request = request; 449Result CleanupClientMap(KSessionRequest* request, KProcessPageTable* client_page_table) {
199 } else if (!m_request_list.empty()) { 450 // If there's no client page table, there's nothing to clean up.
200 // Pop the request from the front of the list. 451 R_SUCCEED_IF(client_page_table == nullptr);
201 request = std::addressof(m_request_list.front());
202 m_request_list.pop_front();
203 452
204 // Get thread and event for the request. 453 // Cleanup Send mappings.
205 thread = request->GetThread(); 454 for (size_t i = 0; i < request->GetSendCount(); ++i) {
206 event = request->GetEvent(); 455 R_TRY(client_page_table->CleanupForIpcClient(request->GetSendClientAddress(i),
207 } 456 request->GetSendSize(i),
457 request->GetSendMemoryState(i)));
458 }
459
460 // Cleanup Receive mappings.
461 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
462 R_TRY(client_page_table->CleanupForIpcClient(request->GetReceiveClientAddress(i),
463 request->GetReceiveSize(i),
464 request->GetReceiveMemoryState(i)));
465 }
466
467 // Cleanup Exchange mappings.
468 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
469 R_TRY(client_page_table->CleanupForIpcClient(request->GetExchangeClientAddress(i),
470 request->GetExchangeSize(i),
471 request->GetExchangeMemoryState(i)));
472 }
473
474 R_SUCCEED();
475}
476
477Result CleanupMap(KSessionRequest* request, KProcess* server_process,
478 KProcessPageTable* client_page_table) {
479 // Cleanup the server map.
480 R_TRY(CleanupServerMap(request, server_process));
481
482 // Cleanup the client map.
483 R_TRY(CleanupClientMap(request, client_page_table));
484
485 R_SUCCEED();
486}
487
488Result ProcessReceiveMessageMapAliasDescriptors(int& offset, KProcessPageTable& dst_page_table,
489 KProcessPageTable& src_page_table,
490 const MessageBuffer& dst_msg,
491 const MessageBuffer& src_msg,
492 KSessionRequest* request, KMemoryPermission perm,
493 bool send) {
494 // Get the offset at the start of processing.
495 const int cur_offset = offset;
496
497 // Get the map alias descriptor.
498 MessageBuffer::MapAliasDescriptor src_desc(src_msg, cur_offset);
499 offset += static_cast<int>(MessageBuffer::MapAliasDescriptor::GetDataSize() / sizeof(u32));
500
501 // Extract address/size.
502 const KProcessAddress src_address = src_desc.GetAddress();
503 const size_t size = src_desc.GetSize();
504 KProcessAddress dst_address = 0;
505
506 // Determine the result memory state.
507 KMemoryState dst_state;
508 R_TRY(GetMapAliasMemoryState(dst_state, src_desc.GetAttribute()));
509
510 // Process the buffer, if it has a size.
511 if (size > 0) {
512 // Set up the source pages for ipc.
513 R_TRY(dst_page_table.SetupForIpc(std::addressof(dst_address), size, src_address,
514 src_page_table, perm, dst_state, send));
515
516 // Ensure that we clean up on failure.
517 ON_RESULT_FAILURE {
518 dst_page_table.CleanupForIpcServer(dst_address, size, dst_state);
519 src_page_table.CleanupForIpcClient(src_address, size, dst_state);
520 };
521
522 // Push the appropriate mapping.
523 if (perm == KMemoryPermission::UserRead) {
524 R_TRY(request->PushSend(src_address, dst_address, size, dst_state));
525 } else if (send) {
526 R_TRY(request->PushExchange(src_address, dst_address, size, dst_state));
527 } else {
528 R_TRY(request->PushReceive(src_address, dst_address, size, dst_state));
208 } 529 }
530 }
209 531
210 // If there are no requests, we're done. 532 // Set the output descriptor.
211 if (request == nullptr) { 533 dst_msg.Set(cur_offset,
212 break; 534 MessageBuffer::MapAliasDescriptor(reinterpret_cast<void*>(GetInteger(dst_address)),
535 size, src_desc.GetAttribute()));
536
537 R_SUCCEED();
538}
539
540Result ReceiveMessage(KernelCore& kernel, bool& recv_list_broken, uint64_t dst_message_buffer,
541 size_t dst_buffer_size, KPhysicalAddress dst_message_paddr,
542 KThread& src_thread, uint64_t src_message_buffer, size_t src_buffer_size,
543 KServerSession* session, KSessionRequest* request) {
544 // Prepare variables for receive.
545 KThread& dst_thread = GetCurrentThread(kernel);
546 KProcess& dst_process = *(dst_thread.GetOwnerProcess());
547 KProcess& src_process = *(src_thread.GetOwnerProcess());
548 auto& dst_page_table = dst_process.GetPageTable();
549 auto& src_page_table = src_process.GetPageTable();
550
551 // NOTE: Session is used only for debugging, and so may go unused.
552 (void)session;
553
554 // The receive list is initially not broken.
555 recv_list_broken = false;
556
557 // Set the server process for the request.
558 request->SetServerProcess(std::addressof(dst_process));
559
560 // Determine the message buffers.
561 u32 *dst_msg_ptr, *src_msg_ptr;
562 bool dst_user, src_user;
563
564 if (dst_message_buffer) {
565 dst_msg_ptr = kernel.System().DeviceMemory().GetPointer<u32>(dst_message_paddr);
566 dst_user = true;
567 } else {
568 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
569 dst_buffer_size = MessageBufferSize;
570 dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
571 dst_user = false;
572 }
573
574 if (src_message_buffer) {
575 // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
576 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
577 src_user = true;
578 } else {
579 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
580 src_buffer_size = MessageBufferSize;
581 src_message_buffer = GetInteger(src_thread.GetTlsAddress());
582 src_user = false;
583 }
584
585 // Parse the headers.
586 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
587 const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
588 const MessageBuffer::MessageHeader dst_header(dst_msg);
589 const MessageBuffer::MessageHeader src_header(src_msg);
590 const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
591 const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
592
593 // Get the end of the source message.
594 const size_t src_end_offset =
595 MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
596
597 // Ensure that the headers fit.
598 R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <= dst_buffer_size,
599 ResultInvalidCombination);
600 R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <= src_buffer_size,
601 ResultInvalidCombination);
602
603 // Ensure the receive list offset is after the end of raw data.
604 if (dst_header.GetReceiveListOffset()) {
605 R_UNLESS(dst_header.GetReceiveListOffset() >=
606 MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
607 dst_header.GetRawCount(),
608 ResultInvalidCombination);
609 }
610
611 // Ensure that the destination buffer is big enough to receive the source.
612 R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
613
614 // Get the receive list.
615 const s32 dst_recv_list_idx =
616 MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
617 ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
618 dst_special_header, dst_buffer_size, src_end_offset,
619 dst_recv_list_idx, !dst_user);
620
621 // Ensure that the source special header isn't invalid.
622 const bool src_has_special_header = src_header.GetHasSpecialHeader();
623 if (src_has_special_header) {
624 // Sending move handles from client -> server is not allowed.
625 R_UNLESS(src_special_header.GetMoveHandleCount() == 0, ResultInvalidCombination);
626 }
627
628 // Prepare for further processing.
629 int pointer_key = 0;
630 int offset = dst_msg.Set(src_header);
631
632 // Set up a guard to make sure that we end up in a clean state on error.
633 ON_RESULT_FAILURE {
634 // Cleanup mappings.
635 CleanupMap(request, std::addressof(dst_process), std::addressof(src_page_table));
636
637 // Cleanup special data.
638 if (src_header.GetHasSpecialHeader()) {
639 CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
213 } 640 }
214 641
215 // All requests must have threads. 642 // Cleanup the header if the receive list isn't broken.
216 ASSERT(thread != nullptr); 643 if (!recv_list_broken) {
644 dst_msg.Set(dst_header);
645 if (dst_header.GetHasSpecialHeader()) {
646 dst_msg.Set(dst_special_header);
647 }
648 }
649 };
650
651 // Process any special data.
652 if (src_header.GetHasSpecialHeader()) {
653 // After we process, make sure we track whether the receive list is broken.
654 SCOPE_EXIT({
655 if (offset > dst_recv_list_idx) {
656 recv_list_broken = true;
657 }
658 });
217 659
218 // Ensure that we close the request when done. 660 // Process special data.
219 SCOPE_EXIT({ request->Close(); }); 661 R_TRY(ProcessMessageSpecialData<false>(offset, dst_process, src_process, src_thread,
662 dst_msg, src_msg, src_special_header));
663 }
220 664
221 // If we're terminating, close a reference to the thread and event. 665 // Process any pointer buffers.
222 if (terminate) { 666 for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
223 thread->Close(); 667 // After we process, make sure we track whether the receive list is broken.
224 if (event != nullptr) { 668 SCOPE_EXIT({
225 event->Close(); 669 if (offset > dst_recv_list_idx) {
670 recv_list_broken = true;
671 }
672 });
673
674 R_TRY(ProcessReceiveMessagePointerDescriptors(
675 offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list,
676 dst_user && dst_header.GetReceiveListCount() ==
677 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
678 }
679
680 // Process any map alias buffers.
681 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
682 // After we process, make sure we track whether the receive list is broken.
683 SCOPE_EXIT({
684 if (offset > dst_recv_list_idx) {
685 recv_list_broken = true;
226 } 686 }
687 });
688
689 // We process in order send, recv, exch. Buffers after send (recv/exch) are ReadWrite.
690 const KMemoryPermission perm = (i >= src_header.GetSendCount())
691 ? KMemoryPermission::UserReadWrite
692 : KMemoryPermission::UserRead;
693
694 // Buffer is send if it is send or exch.
695 const bool send = (i < src_header.GetSendCount()) ||
696 (i >= src_header.GetSendCount() + src_header.GetReceiveCount());
697
698 R_TRY(ProcessReceiveMessageMapAliasDescriptors(offset, dst_page_table, src_page_table,
699 dst_msg, src_msg, request, perm, send));
700 }
701
702 // Process any raw data.
703 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
704 // After we process, make sure we track whether the receive list is broken.
705 SCOPE_EXIT({
706 if (offset + raw_count > dst_recv_list_idx) {
707 recv_list_broken = true;
708 }
709 });
710
711 // Get the offset and size.
712 const size_t offset_words = offset * sizeof(u32);
713 const size_t raw_size = raw_count * sizeof(u32);
714
715 if (!dst_user && !src_user) {
716 // Fast case is TLS -> TLS, do raw memcpy if we can.
717 std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
718 } else if (dst_user) {
719 // Determine how much fast size we can copy.
720 const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
721 const size_t fast_size = max_fast_size - offset_words;
722
723 // Determine source state; if user buffer, we require heap, and otherwise only linear
724 // mapped (to enable tls use).
725 const auto src_state =
726 src_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
727
728 // Determine the source permission. User buffer should be unmapped + read, TLS should be
729 // user readable.
730 const KMemoryPermission src_perm = static_cast<KMemoryPermission>(
731 src_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelRead
732 : KMemoryPermission::UserRead);
733
734 // Perform the fast part of the copy.
735 R_TRY(src_page_table.CopyMemoryFromLinearToKernel(
736 dst_msg_ptr + offset, fast_size, src_message_buffer + offset_words, src_state,
737 src_state, src_perm, KMemoryAttribute::Uncached, KMemoryAttribute::None));
738
739 // If the fast part of the copy didn't get everything, perform the slow part of the
740 // copy.
741 if (fast_size < raw_size) {
742 R_TRY(src_page_table.CopyMemoryFromHeapToHeap(
743 dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
744 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
745 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite,
746 KMemoryAttribute::Uncached | KMemoryAttribute::Locked, KMemoryAttribute::Locked,
747 src_message_buffer + max_fast_size, src_state, src_state, src_perm,
748 KMemoryAttribute::Uncached, KMemoryAttribute::None));
749 }
750 } else /* if (src_user) */ {
751 // The source is a user buffer, so it should be unmapped + readable.
752 constexpr KMemoryPermission SourcePermission = static_cast<KMemoryPermission>(
753 KMemoryPermission::NotMapped | KMemoryPermission::KernelRead);
754
755 // Copy the memory.
756 R_TRY(src_page_table.CopyMemoryFromLinearToUser(
757 dst_message_buffer + offset_words, raw_size, src_message_buffer + offset_words,
758 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
759 SourcePermission, KMemoryAttribute::Uncached, KMemoryAttribute::None));
227 } 760 }
761 }
228 762
229 // If we need to, reply. 763 // We succeeded!
230 if (event != nullptr && !cur_request) { 764 R_SUCCEED();
231 // There must be no mappings. 765}
232 ASSERT(request->GetSendCount() == 0);
233 ASSERT(request->GetReceiveCount() == 0);
234 ASSERT(request->GetExchangeCount() == 0);
235 766
236 // // Get the process and page table. 767Result ProcessSendMessageReceiveMapping(KProcessPageTable& src_page_table,
237 // KProcess *client_process = thread->GetOwnerProcess(); 768 KProcessPageTable& dst_page_table,
238 // auto& client_pt = client_process->GetPageTable(); 769 KProcessAddress client_address,
770 KProcessAddress server_address, size_t size,
771 KMemoryState src_state) {
772 // If the size is zero, there's nothing to process.
773 R_SUCCEED_IF(size == 0);
774
775 // Get the memory state and attribute mask to test.
776 KMemoryState test_state;
777 KMemoryAttribute test_attr_mask;
778 R_TRY(GetMapAliasTestStateAndAttributeMask(test_state, test_attr_mask, src_state));
779
780 // Determine buffer extents.
781 KProcessAddress aligned_dst_start = Common::AlignDown(GetInteger(client_address), PageSize);
782 KProcessAddress aligned_dst_end = Common::AlignUp(GetInteger(client_address) + size, PageSize);
783 KProcessAddress mapping_dst_start = Common::AlignUp(GetInteger(client_address), PageSize);
784 KProcessAddress mapping_dst_end =
785 Common::AlignDown(GetInteger(client_address) + size, PageSize);
786
787 KProcessAddress mapping_src_end =
788 Common::AlignDown(GetInteger(server_address) + size, PageSize);
789
790 // If the start of the buffer is unaligned, handle that.
791 if (aligned_dst_start != mapping_dst_start) {
792 ASSERT(client_address < mapping_dst_start);
793 const size_t copy_size = std::min<size_t>(size, mapping_dst_start - client_address);
794 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
795 client_address, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
796 test_attr_mask, KMemoryAttribute::None, server_address));
797 }
239 798
240 // // Reply to the request. 799 // If the end of the buffer is unaligned, handle that.
241 // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(), 800 if (mapping_dst_end < aligned_dst_end &&
242 // ResultSessionClosed); 801 (aligned_dst_start == mapping_dst_start || aligned_dst_start < mapping_dst_end)) {
802 const size_t copy_size = client_address + size - mapping_dst_end;
803 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
804 mapping_dst_end, copy_size, test_state, test_state, KMemoryPermission::UserReadWrite,
805 test_attr_mask, KMemoryAttribute::None, mapping_src_end));
806 }
243 807
244 // // Unlock the buffer. 808 R_SUCCEED();
245 // // NOTE: Nintendo does not check the result of this. 809}
246 // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
247 810
248 // Signal the event. 811Result ProcessSendMessagePointerDescriptors(int& offset, int& pointer_key,
249 event->Signal(); 812 KProcessPageTable& src_page_table,
813 KProcessPageTable& dst_page_table,
814 const MessageBuffer& dst_msg,
815 const MessageBuffer& src_msg,
816 const ReceiveList& dst_recv_list, bool dst_user) {
817 // Get the offset at the start of processing.
818 const int cur_offset = offset;
819
820 // Get the pointer desc.
821 MessageBuffer::PointerDescriptor src_desc(src_msg, cur_offset);
822 offset += static_cast<int>(MessageBuffer::PointerDescriptor::GetDataSize() / sizeof(u32));
823
824 // Extract address/size.
825 const uint64_t src_pointer = src_desc.GetAddress();
826 const size_t recv_size = src_desc.GetSize();
827 uint64_t recv_pointer = 0;
828
829 // Process the buffer, if it has a size.
830 if (recv_size > 0) {
831 // If using indexing, set index.
832 if (dst_recv_list.IsIndex()) {
833 pointer_key = src_desc.GetIndex();
250 } 834 }
835
836 // Get the buffer.
837 dst_recv_list.GetBuffer(recv_pointer, recv_size, pointer_key);
838 R_UNLESS(recv_pointer != 0, ResultOutOfResource);
839
840 // Perform the pointer data copy.
841 const bool dst_heap = dst_user && dst_recv_list.IsToMessageBuffer();
842 const auto dst_state =
843 dst_heap ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
844 const KMemoryPermission dst_perm =
845 dst_heap ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
846 : KMemoryPermission::UserReadWrite;
847 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
848 recv_pointer, recv_size, dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
849 KMemoryAttribute::None, src_pointer));
251 } 850 }
252 851
253 // Notify. 852 // Set the output descriptor.
254 this->NotifyAvailable(ResultSessionClosed); 853 dst_msg.Set(cur_offset, MessageBuffer::PointerDescriptor(reinterpret_cast<void*>(recv_pointer),
854 recv_size, src_desc.GetIndex()));
855
856 R_SUCCEED();
255} 857}
256 858
257bool KServerSession::IsSignaled() const { 859Result SendMessage(KernelCore& kernel, uint64_t src_message_buffer, size_t src_buffer_size,
258 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel)); 860 KPhysicalAddress src_message_paddr, KThread& dst_thread,
861 uint64_t dst_message_buffer, size_t dst_buffer_size, KServerSession* session,
862 KSessionRequest* request) {
863 // Prepare variables for send.
864 KThread& src_thread = GetCurrentThread(kernel);
865 KProcess& dst_process = *(dst_thread.GetOwnerProcess());
866 KProcess& src_process = *(src_thread.GetOwnerProcess());
867 auto& dst_page_table = dst_process.GetPageTable();
868 auto& src_page_table = src_process.GetPageTable();
869
870 // NOTE: Session is used only for debugging, and so may go unused.
871 (void)session;
872
873 // Determine the message buffers.
874 u32 *dst_msg_ptr, *src_msg_ptr;
875 bool dst_user, src_user;
876
877 if (dst_message_buffer) {
878 // NOTE: Nintendo does not check the result of this GetPhysicalAddress call.
879 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_message_buffer);
880 dst_user = true;
881 } else {
882 dst_msg_ptr = dst_page_table.GetMemory().GetPointer<u32>(dst_thread.GetTlsAddress());
883 dst_buffer_size = MessageBufferSize;
884 dst_message_buffer = GetInteger(dst_thread.GetTlsAddress());
885 dst_user = false;
886 }
259 887
260 // If the client is closed, we're always signaled. 888 if (src_message_buffer) {
261 if (m_parent->IsClientClosed()) { 889 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_message_buffer);
262 return true; 890 src_user = true;
891 } else {
892 src_msg_ptr = src_page_table.GetMemory().GetPointer<u32>(src_thread.GetTlsAddress());
893 src_buffer_size = MessageBufferSize;
894 src_message_buffer = GetInteger(src_thread.GetTlsAddress());
895 src_user = false;
263 } 896 }
264 897
265 // Otherwise, we're signaled if we have a request and aren't handling one. 898 // Parse the headers.
266 return !m_request_list.empty() && m_current_request == nullptr; 899 const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size);
900 const MessageBuffer src_msg(src_msg_ptr, src_buffer_size);
901 const MessageBuffer::MessageHeader dst_header(dst_msg);
902 const MessageBuffer::MessageHeader src_header(src_msg);
903 const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header);
904 const MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
905
906 // Get the end of the source message.
907 const size_t src_end_offset =
908 MessageBuffer::GetRawDataIndex(src_header, src_special_header) + src_header.GetRawCount();
909
910 // Declare variables for processing.
911 int offset = 0;
912 int pointer_key = 0;
913 bool processed_special_data = false;
914
915 // Send the message.
916 {
917 // Make sure that we end up in a clean state on error.
918 ON_RESULT_FAILURE {
919 // Cleanup special data.
920 if (processed_special_data) {
921 if (src_header.GetHasSpecialHeader()) {
922 CleanupSpecialData(dst_process, dst_msg_ptr, dst_buffer_size);
923 }
924 } else {
925 CleanupServerHandles(kernel, src_user ? src_message_buffer : 0, src_buffer_size,
926 src_message_paddr);
927 }
928
929 // Cleanup mappings.
930 CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table));
931 };
932
933 // Ensure that the headers fit.
934 R_UNLESS(MessageBuffer::GetMessageBufferSize(src_header, src_special_header) <=
935 src_buffer_size,
936 ResultInvalidCombination);
937 R_UNLESS(MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) <=
938 dst_buffer_size,
939 ResultInvalidCombination);
940
941 // Ensure the receive list offset is after the end of raw data.
942 if (dst_header.GetReceiveListOffset()) {
943 R_UNLESS(dst_header.GetReceiveListOffset() >=
944 MessageBuffer::GetRawDataIndex(dst_header, dst_special_header) +
945 dst_header.GetRawCount(),
946 ResultInvalidCombination);
947 }
948
949 // Ensure that the destination buffer is big enough to receive the source.
950 R_UNLESS(dst_buffer_size >= src_end_offset * sizeof(u32), ResultMessageTooLarge);
951
952 // Replies must have no buffers.
953 R_UNLESS(src_header.GetSendCount() == 0, ResultInvalidCombination);
954 R_UNLESS(src_header.GetReceiveCount() == 0, ResultInvalidCombination);
955 R_UNLESS(src_header.GetExchangeCount() == 0, ResultInvalidCombination);
956
957 // Get the receive list.
958 const s32 dst_recv_list_idx =
959 MessageBuffer::GetReceiveListIndex(dst_header, dst_special_header);
960 ReceiveList dst_recv_list(dst_msg_ptr, dst_message_buffer, dst_page_table, dst_header,
961 dst_special_header, dst_buffer_size, src_end_offset,
962 dst_recv_list_idx, !dst_user);
963
964 // Handle any receive buffers.
965 for (size_t i = 0; i < request->GetReceiveCount(); ++i) {
966 R_TRY(ProcessSendMessageReceiveMapping(
967 src_page_table, dst_page_table, request->GetReceiveClientAddress(i),
968 request->GetReceiveServerAddress(i), request->GetReceiveSize(i),
969 request->GetReceiveMemoryState(i)));
970 }
971
972 // Handle any exchange buffers.
973 for (size_t i = 0; i < request->GetExchangeCount(); ++i) {
974 R_TRY(ProcessSendMessageReceiveMapping(
975 src_page_table, dst_page_table, request->GetExchangeClientAddress(i),
976 request->GetExchangeServerAddress(i), request->GetExchangeSize(i),
977 request->GetExchangeMemoryState(i)));
978 }
979
980 // Set the header.
981 offset = dst_msg.Set(src_header);
982
983 // Process any special data.
984 ASSERT(GetCurrentThreadPointer(kernel) == std::addressof(src_thread));
985 processed_special_data = true;
986 if (src_header.GetHasSpecialHeader()) {
987 R_TRY(ProcessMessageSpecialData<true>(offset, dst_process, src_process, src_thread,
988 dst_msg, src_msg, src_special_header));
989 }
990
991 // Process any pointer buffers.
992 for (auto i = 0; i < src_header.GetPointerCount(); ++i) {
993 R_TRY(ProcessSendMessagePointerDescriptors(
994 offset, pointer_key, src_page_table, dst_page_table, dst_msg, src_msg,
995 dst_recv_list,
996 dst_user &&
997 dst_header.GetReceiveListCount() ==
998 MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer));
999 }
1000
1001 // Clear any map alias buffers.
1002 for (auto i = 0; i < src_header.GetMapAliasCount(); ++i) {
1003 offset = dst_msg.Set(offset, MessageBuffer::MapAliasDescriptor());
1004 }
1005
1006 // Process any raw data.
1007 if (const auto raw_count = src_header.GetRawCount(); raw_count != 0) {
1008 // Get the offset and size.
1009 const size_t offset_words = offset * sizeof(u32);
1010 const size_t raw_size = raw_count * sizeof(u32);
1011
1012 if (!dst_user && !src_user) {
1013 // Fast case is TLS -> TLS, do raw memcpy if we can.
1014 std::memcpy(dst_msg_ptr + offset, src_msg_ptr + offset, raw_size);
1015 } else if (src_user) {
1016 // Determine how much fast size we can copy.
1017 const size_t max_fast_size = std::min<size_t>(offset_words + raw_size, PageSize);
1018 const size_t fast_size = max_fast_size - offset_words;
1019
1020 // Determine dst state; if user buffer, we require heap, and otherwise only linear
1021 // mapped (to enable tls use).
1022 const auto dst_state =
1023 dst_user ? KMemoryState::FlagReferenceCounted : KMemoryState::FlagLinearMapped;
1024
1025 // Determine the dst permission. User buffer should be unmapped + read, TLS should
1026 // be user readable.
1027 const KMemoryPermission dst_perm =
1028 dst_user ? KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite
1029 : KMemoryPermission::UserReadWrite;
1030
1031 // Perform the fast part of the copy.
1032 R_TRY(dst_page_table.CopyMemoryFromKernelToLinear(
1033 dst_message_buffer + offset_words, fast_size, dst_state, dst_state, dst_perm,
1034 KMemoryAttribute::Uncached, KMemoryAttribute::None, src_msg_ptr + offset));
1035
1036 // If the fast part of the copy didn't get everything, perform the slow part of the
1037 // copy.
1038 if (fast_size < raw_size) {
1039 R_TRY(dst_page_table.CopyMemoryFromHeapToHeap(
1040 dst_page_table, dst_message_buffer + max_fast_size, raw_size - fast_size,
1041 dst_state, dst_state, dst_perm, KMemoryAttribute::Uncached,
1042 KMemoryAttribute::None, src_message_buffer + max_fast_size,
1043 KMemoryState::FlagReferenceCounted, KMemoryState::FlagReferenceCounted,
1044 KMemoryPermission::NotMapped | KMemoryPermission::KernelRead,
1045 KMemoryAttribute::Uncached | KMemoryAttribute::Locked,
1046 KMemoryAttribute::Locked));
1047 }
1048 } else /* if (dst_user) */ {
1049 // The destination is a user buffer, so it should be unmapped + readable.
1050 constexpr KMemoryPermission DestinationPermission =
1051 KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
1052
1053 // Copy the memory.
1054 R_TRY(dst_page_table.CopyMemoryFromUserToLinear(
1055 dst_message_buffer + offset_words, raw_size, KMemoryState::FlagReferenceCounted,
1056 KMemoryState::FlagReferenceCounted, DestinationPermission,
1057 KMemoryAttribute::Uncached, KMemoryAttribute::None,
1058 src_message_buffer + offset_words));
1059 }
1060 }
1061 }
1062
1063 // Perform (and validate) any remaining cleanup.
1064 R_RETURN(CleanupMap(request, std::addressof(src_process), std::addressof(dst_page_table)));
267} 1065}
268 1066
269Result KServerSession::OnRequest(KSessionRequest* request) { 1067void ReplyAsyncError(KProcess* to_process, uint64_t to_msg_buf, size_t to_msg_buf_size,
270 // Create the wait queue. 1068 Result result) {
271 ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel}; 1069 // Convert the address to a linear pointer.
1070 u32* to_msg = to_process->GetMemory().GetPointer<u32>(to_msg_buf);
1071
1072 // Set the error.
1073 MessageBuffer msg(to_msg, to_msg_buf_size);
1074 msg.SetAsyncResult(result);
1075}
1076
1077} // namespace
1078
1079KServerSession::KServerSession(KernelCore& kernel)
1080 : KSynchronizationObject{kernel}, m_lock{m_kernel} {}
1081
1082KServerSession::~KServerSession() = default;
1083
1084void KServerSession::Destroy() {
1085 m_parent->OnServerClosed();
1086
1087 this->CleanupRequests();
1088
1089 m_parent->Close();
1090}
1091
1092Result KServerSession::ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
1093 KPhysicalAddress server_message_paddr,
1094 std::shared_ptr<Service::HLERequestContext>* out_context,
1095 std::weak_ptr<Service::SessionRequestManager> manager) {
1096 // Lock the session.
1097 KScopedLightLock lk{m_lock};
1098
1099 // Get the request and client thread.
1100 KSessionRequest* request;
1101 KThread* client_thread;
272 1102
273 { 1103 {
274 // Lock the scheduler.
275 KScopedSchedulerLock sl{m_kernel}; 1104 KScopedSchedulerLock sl{m_kernel};
276 1105
277 // Ensure that we can handle new requests. 1106 // Ensure that we can service the request.
278 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); 1107 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
279 1108
280 // Check that we're not terminating. 1109 // Ensure we aren't already servicing a request.
281 R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested); 1110 R_UNLESS(m_current_request == nullptr, ResultNotFound);
282 1111
283 // Get whether we're empty. 1112 // Ensure we have a request to service.
284 const bool was_empty = m_request_list.empty(); 1113 R_UNLESS(!m_request_list.empty(), ResultNotFound);
285 1114
286 // Add the request to the list. 1115 // Pop the first request from the list.
287 request->Open(); 1116 request = std::addressof(m_request_list.front());
288 m_request_list.push_back(*request); 1117 m_request_list.pop_front();
289 1118
290 // If we were empty, signal. 1119 // Get the thread for the request.
291 if (was_empty) { 1120 client_thread = request->GetThread();
292 this->NotifyAvailable(); 1121 R_UNLESS(client_thread != nullptr, ResultSessionClosed);
1122
1123 // Open the client thread.
1124 client_thread->Open();
1125 }
1126
1127 SCOPE_EXIT({ client_thread->Close(); });
1128
1129 // Set the request as our current.
1130 m_current_request = request;
1131
1132 // Get the client address.
1133 uint64_t client_message = request->GetAddress();
1134 size_t client_buffer_size = request->GetSize();
1135 bool recv_list_broken = false;
1136
1137 // Receive the message.
1138 Result result = ResultSuccess;
1139
1140 if (out_context != nullptr) {
1141 // HLE request.
1142 if (!client_message) {
1143 client_message = GetInteger(client_thread->GetTlsAddress());
293 } 1144 }
1145 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
1146 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
1147 *out_context =
1148 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
1149 (*out_context)->SetSessionRequestManager(manager);
1150 (*out_context)->PopulateFromIncomingCommandBuffer(cmd_buf);
1151 // We succeeded.
1152 R_SUCCEED();
1153 } else {
1154 result = ReceiveMessage(m_kernel, recv_list_broken, server_message, server_buffer_size,
1155 server_message_paddr, *client_thread, client_message,
1156 client_buffer_size, this, request);
1157 }
294 1158
295 // If we have a request event, this is asynchronous, and we don't need to wait. 1159 // Handle cleanup on receive failure.
296 R_SUCCEED_IF(request->GetEvent() != nullptr); 1160 if (R_FAILED(result)) {
1161 // Cache the result to return it to the client.
1162 const Result result_for_client = result;
297 1163
298 // This is a synchronous request, so we should wait for our request to complete. 1164 // Clear the current request.
299 GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); 1165 {
300 GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); 1166 KScopedSchedulerLock sl(m_kernel);
1167 ASSERT(m_current_request == request);
1168 m_current_request = nullptr;
1169 if (!m_request_list.empty()) {
1170 this->NotifyAvailable();
1171 }
1172 }
1173
1174 // Reply to the client.
1175 {
1176 // After we reply, close our reference to the request.
1177 SCOPE_EXIT({ request->Close(); });
1178
1179 // Get the event to check whether the request is async.
1180 if (KEvent* event = request->GetEvent(); event != nullptr) {
1181 // The client sent an async request.
1182 KProcess* client = client_thread->GetOwnerProcess();
1183 auto& client_pt = client->GetPageTable();
1184
1185 // Send the async result.
1186 if (R_FAILED(result_for_client)) {
1187 ReplyAsyncError(client, client_message, client_buffer_size, result_for_client);
1188 }
1189
1190 // Unlock the client buffer.
1191 // NOTE: Nintendo does not check the result of this.
1192 client_pt.UnlockForIpcUserBuffer(client_message, client_buffer_size);
1193
1194 // Signal the event.
1195 event->Signal();
1196 } else {
1197 // End the client thread's wait.
1198 KScopedSchedulerLock sl(m_kernel);
1199
1200 if (!client_thread->IsTerminationRequested()) {
1201 client_thread->EndWait(result_for_client);
1202 }
1203 }
1204 }
1205
1206 // Set the server result.
1207 if (recv_list_broken) {
1208 result = ResultReceiveListBroken;
1209 } else {
1210 result = ResultNotFound;
1211 }
301 } 1212 }
302 1213
303 return GetCurrentThread(m_kernel).GetWaitResult(); 1214 R_RETURN(result);
304} 1215}
305 1216
306Result KServerSession::SendReply(bool is_hle) { 1217Result KServerSession::SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
1218 KPhysicalAddress server_message_paddr, bool is_hle) {
307 // Lock the session. 1219 // Lock the session.
308 KScopedLightLock lk{m_lock}; 1220 KScopedLightLock lk{m_lock};
309 1221
@@ -327,7 +1239,7 @@ Result KServerSession::SendReply(bool is_hle) {
327 SCOPE_EXIT({ request->Close(); }); 1239 SCOPE_EXIT({ request->Close(); });
328 1240
329 // Extract relevant information from the request. 1241 // Extract relevant information from the request.
330 const uintptr_t client_message = request->GetAddress(); 1242 const uint64_t client_message = request->GetAddress();
331 const size_t client_buffer_size = request->GetSize(); 1243 const size_t client_buffer_size = request->GetSize();
332 KThread* client_thread = request->GetThread(); 1244 KThread* client_thread = request->GetThread();
333 KEvent* event = request->GetEvent(); 1245 KEvent* event = request->GetEvent();
@@ -342,31 +1254,28 @@ Result KServerSession::SendReply(bool is_hle) {
342 // HLE servers write directly to a pointer to the thread command buffer. Therefore 1254 // HLE servers write directly to a pointer to the thread command buffer. Therefore
343 // the reply has already been written in this case. 1255 // the reply has already been written in this case.
344 } else { 1256 } else {
345 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 1257 result = SendMessage(m_kernel, server_message, server_buffer_size, server_message_paddr,
346 KThread* server_thread = GetCurrentThreadPointer(m_kernel); 1258 *client_thread, client_message, client_buffer_size, this, request);
347 KProcess& src_process = *client_thread->GetOwnerProcess(); 1259 }
348 KProcess& dst_process = *server_thread->GetOwnerProcess(); 1260 } else if (!is_hle) {
349 UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); 1261 // Otherwise, we'll need to do some cleanup.
350 1262 KProcess* server_process = request->GetServerProcess();
351 auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress()); 1263 KProcess* client_process =
352 auto* dst_msg_buffer = memory.GetPointer<u32>(client_message); 1264 (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
353 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); 1265 KProcessPageTable* client_page_table =
354 1266 (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
355 // Translate special header ad-hoc. 1267
356 MessageBuffer src_msg(src_msg_buffer, client_buffer_size); 1268 // Cleanup server handles.
357 MessageBuffer::MessageHeader src_header(src_msg); 1269 result = CleanupServerHandles(m_kernel, server_message, server_buffer_size,
358 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header); 1270 server_message_paddr);
359 if (src_header.GetHasSpecialHeader()) { 1271
360 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size); 1272 // Cleanup mappings.
361 result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread, 1273 Result cleanup_map_result = CleanupMap(request, server_process, client_page_table);
362 dst_msg, src_msg, src_special_header); 1274
363 if (R_FAILED(result)) { 1275 // If we successfully cleaned up handles, use the map cleanup result as our result.
364 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size); 1276 if (R_SUCCEEDED(result)) {
365 } 1277 result = cleanup_map_result;
366 }
367 } 1278 }
368 } else {
369 result = ResultSessionClosed;
370 } 1279 }
371 1280
372 // Select a result for the client. 1281 // Select a result for the client.
@@ -381,19 +1290,18 @@ Result KServerSession::SendReply(bool is_hle) {
381 // If there's a client thread, update it. 1290 // If there's a client thread, update it.
382 if (client_thread != nullptr) { 1291 if (client_thread != nullptr) {
383 if (event != nullptr) { 1292 if (event != nullptr) {
384 // // Get the client process/page table. 1293 // Get the client process/page table.
385 // KProcess *client_process = client_thread->GetOwnerProcess(); 1294 KProcess* client_process = client_thread->GetOwnerProcess();
386 // KProcessPageTable *client_page_table = std::addressof(client_process->PageTable()); 1295 KProcessPageTable* client_page_table = std::addressof(client_process->GetPageTable());
387 1296
388 // // If we need to, reply with an async error. 1297 // If we need to, reply with an async error.
389 // if (R_FAILED(client_result)) { 1298 if (R_FAILED(client_result)) {
390 // ReplyAsyncError(client_process, client_message, client_buffer_size, 1299 ReplyAsyncError(client_process, client_message, client_buffer_size, client_result);
391 // client_result); 1300 }
392 // }
393 1301
394 // // Unlock the client buffer. 1302 // Unlock the client buffer.
395 // // NOTE: Nintendo does not check the result of this. 1303 // NOTE: Nintendo does not check the result of this.
396 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); 1304 client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
397 1305
398 // Signal the event. 1306 // Signal the event.
399 event->Signal(); 1307 event->Signal();
@@ -410,91 +1318,53 @@ Result KServerSession::SendReply(bool is_hle) {
410 R_RETURN(result); 1318 R_RETURN(result);
411} 1319}
412 1320
413Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context, 1321Result KServerSession::OnRequest(KSessionRequest* request) {
414 std::weak_ptr<Service::SessionRequestManager> manager) { 1322 // Create the wait queue.
415 // Lock the session. 1323 ThreadQueueImplForKServerSessionRequest wait_queue{m_kernel};
416 KScopedLightLock lk{m_lock};
417
418 // Get the request and client thread.
419 KSessionRequest* request;
420 KThread* client_thread;
421 1324
422 { 1325 {
1326 // Lock the scheduler.
423 KScopedSchedulerLock sl{m_kernel}; 1327 KScopedSchedulerLock sl{m_kernel};
424 1328
425 // Ensure that we can service the request. 1329 // Ensure that we can handle new requests.
426 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); 1330 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
427
428 // Ensure we aren't already servicing a request.
429 R_UNLESS(m_current_request == nullptr, ResultNotFound);
430 1331
431 // Ensure we have a request to service. 1332 // Check that we're not terminating.
432 R_UNLESS(!m_request_list.empty(), ResultNotFound); 1333 R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), ResultTerminationRequested);
433 1334
434 // Pop the first request from the list. 1335 // Get whether we're empty.
435 request = std::addressof(m_request_list.front()); 1336 const bool was_empty = m_request_list.empty();
436 m_request_list.pop_front();
437 1337
438 // Get the thread for the request. 1338 // Add the request to the list.
439 client_thread = request->GetThread(); 1339 request->Open();
440 R_UNLESS(client_thread != nullptr, ResultSessionClosed); 1340 m_request_list.push_back(*request);
441 1341
442 // Open the client thread. 1342 // If we were empty, signal.
443 client_thread->Open(); 1343 if (was_empty) {
444 } 1344 this->NotifyAvailable();
1345 }
445 1346
446 SCOPE_EXIT({ client_thread->Close(); }); 1347 // If we have a request event, this is asynchronous, and we don't need to wait.
1348 R_SUCCEED_IF(request->GetEvent() != nullptr);
447 1349
448 // Set the request as our current. 1350 // This is a synchronous request, so we should wait for our request to complete.
449 m_current_request = request; 1351 GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
1352 GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
1353 }
450 1354
451 // Get the client address. 1355 return GetCurrentThread(m_kernel).GetWaitResult();
452 uintptr_t client_message = request->GetAddress(); 1356}
453 size_t client_buffer_size = request->GetSize();
454 // bool recv_list_broken = false;
455 1357
456 if (!client_message) { 1358bool KServerSession::IsSignaled() const {
457 client_message = GetInteger(client_thread->GetTlsAddress()); 1359 ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(m_kernel));
458 client_buffer_size = MessageBufferSize;
459 }
460 1360
461 // Receive the message. 1361 // If the client is closed, we're always signaled.
462 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 1362 if (m_parent->IsClientClosed()) {
463 if (out_context != nullptr) { 1363 return true;
464 // HLE request.
465 u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
466 *out_context =
467 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
468 (*out_context)->SetSessionRequestManager(manager);
469 (*out_context)
470 ->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
471 } else {
472 KThread* server_thread = GetCurrentThreadPointer(m_kernel);
473 KProcess& src_process = *client_thread->GetOwnerProcess();
474 KProcess& dst_process = *server_thread->GetOwnerProcess();
475 UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess());
476
477 auto* src_msg_buffer = memory.GetPointer<u32>(client_message);
478 auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress());
479 std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
480
481 // Translate special header ad-hoc.
482 // TODO: fix this mess
483 MessageBuffer src_msg(src_msg_buffer, client_buffer_size);
484 MessageBuffer::MessageHeader src_header(src_msg);
485 MessageBuffer::SpecialHeader src_special_header(src_msg, src_header);
486 if (src_header.GetHasSpecialHeader()) {
487 MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size);
488 Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread,
489 dst_msg, src_msg, src_special_header);
490 if (R_FAILED(res)) {
491 CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size);
492 }
493 }
494 } 1364 }
495 1365
496 // We succeeded. 1366 // Otherwise, we're signaled if we have a request and aren't handling one.
497 R_SUCCEED(); 1367 return !m_request_list.empty() && m_current_request == nullptr;
498} 1368}
499 1369
500void KServerSession::CleanupRequests() { 1370void KServerSession::CleanupRequests() {
@@ -527,31 +1397,30 @@ void KServerSession::CleanupRequests() {
527 SCOPE_EXIT({ request->Close(); }); 1397 SCOPE_EXIT({ request->Close(); });
528 1398
529 // Extract relevant information from the request. 1399 // Extract relevant information from the request.
530 // const uintptr_t client_message = request->GetAddress(); 1400 const uint64_t client_message = request->GetAddress();
531 // const size_t client_buffer_size = request->GetSize(); 1401 const size_t client_buffer_size = request->GetSize();
532 KThread* client_thread = request->GetThread(); 1402 KThread* client_thread = request->GetThread();
533 KEvent* event = request->GetEvent(); 1403 KEvent* event = request->GetEvent();
534 1404
535 // KProcess *server_process = request->GetServerProcess(); 1405 KProcess* server_process = request->GetServerProcess();
536 // KProcess *client_process = (client_thread != nullptr) ? 1406 KProcess* client_process =
537 // client_thread->GetOwnerProcess() : nullptr; 1407 (client_thread != nullptr) ? client_thread->GetOwnerProcess() : nullptr;
538 // KProcessPageTable *client_page_table = (client_process != nullptr) ? 1408 KProcessPageTable* client_page_table =
539 // std::addressof(client_process->GetPageTable()) 1409 (client_process != nullptr) ? std::addressof(client_process->GetPageTable()) : nullptr;
540 // : nullptr;
541 1410
542 // Cleanup the mappings. 1411 // Cleanup the mappings.
543 // Result result = CleanupMap(request, server_process, client_page_table); 1412 Result result = CleanupMap(request, server_process, client_page_table);
544 1413
545 // If there's a client thread, update it. 1414 // If there's a client thread, update it.
546 if (client_thread != nullptr) { 1415 if (client_thread != nullptr) {
547 if (event != nullptr) { 1416 if (event != nullptr) {
548 // // We need to reply async. 1417 // We need to reply async.
549 // ReplyAsyncError(client_process, client_message, client_buffer_size, 1418 ReplyAsyncError(client_process, client_message, client_buffer_size,
550 // (R_SUCCEEDED(result) ? ResultSessionClosed : result)); 1419 (R_SUCCEEDED(result) ? ResultSessionClosed : result));
551 1420
552 // // Unlock the client buffer. 1421 // Unlock the client buffer.
553 // NOTE: Nintendo does not check the result of this. 1422 // NOTE: Nintendo does not check the result of this.
554 // client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size); 1423 client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
555 1424
556 // Signal the event. 1425 // Signal the event.
557 event->Signal(); 1426 event->Signal();
@@ -567,4 +1436,97 @@ void KServerSession::CleanupRequests() {
567 } 1436 }
568} 1437}
569 1438
1439void KServerSession::OnClientClosed() {
1440 KScopedLightLock lk{m_lock};
1441
1442 // Handle any pending requests.
1443 KSessionRequest* prev_request = nullptr;
1444 while (true) {
1445 // Declare variables for processing the request.
1446 KSessionRequest* request = nullptr;
1447 KEvent* event = nullptr;
1448 KThread* thread = nullptr;
1449 bool cur_request = false;
1450 bool terminate = false;
1451
1452 // Get the next request.
1453 {
1454 KScopedSchedulerLock sl{m_kernel};
1455
1456 if (m_current_request != nullptr && m_current_request != prev_request) {
1457 // Set the request, open a reference as we process it.
1458 request = m_current_request;
1459 request->Open();
1460 cur_request = true;
1461
1462 // Get thread and event for the request.
1463 thread = request->GetThread();
1464 event = request->GetEvent();
1465
1466 // If the thread is terminating, handle that.
1467 if (thread->IsTerminationRequested()) {
1468 request->ClearThread();
1469 request->ClearEvent();
1470 terminate = true;
1471 }
1472
1473 prev_request = request;
1474 } else if (!m_request_list.empty()) {
1475 // Pop the request from the front of the list.
1476 request = std::addressof(m_request_list.front());
1477 m_request_list.pop_front();
1478
1479 // Get thread and event for the request.
1480 thread = request->GetThread();
1481 event = request->GetEvent();
1482 }
1483 }
1484
1485 // If there are no requests, we're done.
1486 if (request == nullptr) {
1487 break;
1488 }
1489
1490 // All requests must have threads.
1491 ASSERT(thread != nullptr);
1492
1493 // Ensure that we close the request when done.
1494 SCOPE_EXIT({ request->Close(); });
1495
1496 // If we're terminating, close a reference to the thread and event.
1497 if (terminate) {
1498 thread->Close();
1499 if (event != nullptr) {
1500 event->Close();
1501 }
1502 }
1503
1504 // If we need to, reply.
1505 if (event != nullptr && !cur_request) {
1506 // There must be no mappings.
1507 ASSERT(request->GetSendCount() == 0);
1508 ASSERT(request->GetReceiveCount() == 0);
1509 ASSERT(request->GetExchangeCount() == 0);
1510
1511 // Get the process and page table.
1512 KProcess* client_process = thread->GetOwnerProcess();
1513 auto& client_pt = client_process->GetPageTable();
1514
1515 // Reply to the request.
1516 ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
1517 ResultSessionClosed);
1518
1519 // Unlock the buffer.
1520 // NOTE: Nintendo does not check the result of this.
1521 client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
1522
1523 // Signal the event.
1524 event->Signal();
1525 }
1526 }
1527
1528 // Notify.
1529 this->NotifyAvailable(ResultSessionClosed);
1530}
1531
570} // namespace Kernel 1532} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 403891919..2876c231b 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -49,14 +49,21 @@ public:
49 bool IsSignaled() const override; 49 bool IsSignaled() const override;
50 void OnClientClosed(); 50 void OnClientClosed();
51 51
52 /// TODO: flesh these out to match the real kernel
53 Result OnRequest(KSessionRequest* request); 52 Result OnRequest(KSessionRequest* request);
54 Result SendReply(bool is_hle = false); 53 Result SendReply(uintptr_t server_message, uintptr_t server_buffer_size,
55 Result ReceiveRequest(std::shared_ptr<Service::HLERequestContext>* out_context = nullptr, 54 KPhysicalAddress server_message_paddr, bool is_hle = false);
55 Result ReceiveRequest(uintptr_t server_message, uintptr_t server_buffer_size,
56 KPhysicalAddress server_message_paddr,
57 std::shared_ptr<Service::HLERequestContext>* out_context = nullptr,
56 std::weak_ptr<Service::SessionRequestManager> manager = {}); 58 std::weak_ptr<Service::SessionRequestManager> manager = {});
57 59
58 Result SendReplyHLE() { 60 Result SendReplyHLE() {
59 return SendReply(true); 61 R_RETURN(this->SendReply(0, 0, 0, true));
62 }
63
64 Result ReceiveRequestHLE(std::shared_ptr<Service::HLERequestContext>* out_context,
65 std::weak_ptr<Service::SessionRequestManager> manager) {
66 R_RETURN(this->ReceiveRequest(0, 0, 0, out_context, manager));
60 } 67 }
61 68
62private: 69private:
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index 44d7a8f02..4a1f6027e 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -33,8 +33,7 @@ void KSession::Initialize(KClientPort* client_port, uintptr_t name) {
33 m_name = name; 33 m_name = name;
34 34
35 // Set our owner process. 35 // Set our owner process.
36 //! FIXME: this is the wrong process! 36 m_process = GetCurrentProcessPointer(m_kernel);
37 m_process = m_kernel.ApplicationProcess();
38 m_process->Open(); 37 m_process->Open();
39 38
40 // Set our port. 39 // Set our port.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 7d9a6e9cf..7d3421929 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -1258,11 +1258,11 @@ ThreadState KThread::RequestTerminate() {
1258 // Change the thread's priority to be higher than any system thread's. 1258 // Change the thread's priority to be higher than any system thread's.
1259 this->IncreaseBasePriority(TerminatingThreadPriority); 1259 this->IncreaseBasePriority(TerminatingThreadPriority);
1260 1260
1261 // If the thread is runnable, send a termination interrupt to other cores. 1261 // If the thread is runnable, send a termination interrupt to cores it may be running on.
1262 if (this->GetState() == ThreadState::Runnable) { 1262 if (this->GetState() == ThreadState::Runnable) {
1263 if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask() & 1263 // NOTE: We do not mask the "current core", because this code may not actually be
1264 ~(1ULL << GetCurrentCoreId(m_kernel)); 1264 // executing from the thread representing the "current core".
1265 core_mask != 0) { 1265 if (const u64 core_mask = m_physical_affinity_mask.GetAffinityMask(); core_mask != 0) {
1266 Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask); 1266 Kernel::KInterruptManager::SendInterProcessorInterrupt(m_kernel, core_mask);
1267 } 1267 }
1268 } 1268 }
@@ -1422,8 +1422,7 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
1422} 1422}
1423 1423
1424Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) { 1424Core::Memory::Memory& GetCurrentMemory(KernelCore& kernel) {
1425 // TODO: per-process memory 1425 return GetCurrentProcess(kernel).GetMemory();
1426 return kernel.System().ApplicationMemory();
1427} 1426}
1428 1427
1429KScopedDisableDispatch::~KScopedDisableDispatch() { 1428KScopedDisableDispatch::~KScopedDisableDispatch() {
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index e9925d231..f13e232b2 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -314,11 +314,7 @@ public:
314 m_current_core_id = core; 314 m_current_core_id = core;
315 } 315 }
316 316
317 KProcess* GetOwnerProcess() { 317 KProcess* GetOwnerProcess() const {
318 return m_parent;
319 }
320
321 const KProcess* GetOwnerProcess() const {
322 return m_parent; 318 return m_parent;
323 } 319 }
324 320
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index 8a0b08761..530b45218 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -5,6 +5,7 @@
5 5
6#include <optional> 6#include <optional>
7 7
8#include "core/hle/kernel/k_light_lock.h"
8#include "core/hle/kernel/k_page_group.h" 9#include "core/hle/kernel/k_page_group.h"
9#include "core/hle/kernel/slab_helpers.h" 10#include "core/hle/kernel/slab_helpers.h"
10#include "core/hle/kernel/svc_types.h" 11#include "core/hle/kernel/svc_types.h"
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e479dacde..1030f0c12 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -68,8 +68,6 @@ struct KernelCore::Impl {
68 68
69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel); 69 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 70 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
71 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
72 global_handle_table->Initialize(KHandleTable::MaxTableSize);
73 71
74 is_phantom_mode_for_singlecore = false; 72 is_phantom_mode_for_singlecore = false;
75 73
@@ -121,13 +119,8 @@ struct KernelCore::Impl {
121 next_user_process_id = KProcess::ProcessIdMin; 119 next_user_process_id = KProcess::ProcessIdMin;
122 next_thread_id = 1; 120 next_thread_id = 1;
123 121
124 global_handle_table->Finalize();
125 global_handle_table.reset();
126
127 preemption_event = nullptr; 122 preemption_event = nullptr;
128 123
129 exclusive_monitor.reset();
130
131 // Cleanup persistent kernel objects 124 // Cleanup persistent kernel objects
132 auto CleanupObject = [](KAutoObject* obj) { 125 auto CleanupObject = [](KAutoObject* obj) {
133 if (obj) { 126 if (obj) {
@@ -191,8 +184,6 @@ struct KernelCore::Impl {
191 } 184 }
192 185
193 void InitializePhysicalCores() { 186 void InitializePhysicalCores() {
194 exclusive_monitor =
195 Core::MakeExclusiveMonitor(system.ApplicationMemory(), Core::Hardware::NUM_CPU_CORES);
196 for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 187 for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
197 const s32 core{static_cast<s32>(i)}; 188 const s32 core{static_cast<s32>(i)};
198 189
@@ -247,7 +238,7 @@ struct KernelCore::Impl {
247 void InitializePreemption(KernelCore& kernel) { 238 void InitializePreemption(KernelCore& kernel) {
248 preemption_event = Core::Timing::CreateEvent( 239 preemption_event = Core::Timing::CreateEvent(
249 "PreemptionCallback", 240 "PreemptionCallback",
250 [this, &kernel](std::uintptr_t, s64 time, 241 [this, &kernel](s64 time,
251 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> { 242 std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
252 { 243 {
253 KScopedSchedulerLock lock(kernel); 244 KScopedSchedulerLock lock(kernel);
@@ -791,10 +782,6 @@ struct KernelCore::Impl {
791 782
792 std::shared_ptr<Core::Timing::EventType> preemption_event; 783 std::shared_ptr<Core::Timing::EventType> preemption_event;
793 784
794 // This is the kernel's handle table or supervisor handle table which
795 // stores all the objects in place.
796 std::unique_ptr<KHandleTable> global_handle_table;
797
798 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container; 785 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
799 786
800 std::unique_ptr<KObjectNameGlobalData> object_name_global_data; 787 std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
@@ -805,7 +792,6 @@ struct KernelCore::Impl {
805 std::mutex server_lock; 792 std::mutex server_lock;
806 std::vector<std::unique_ptr<Service::ServerManager>> server_managers; 793 std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
807 794
808 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
809 std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores; 795 std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
810 796
811 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others 797 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
@@ -882,10 +868,6 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {
882 return impl->system_resource_limit; 868 return impl->system_resource_limit;
883} 869}
884 870
885KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
886 return impl->global_handle_table->GetObject<KThread>(handle);
887}
888
889void KernelCore::AppendNewProcess(KProcess* process) { 871void KernelCore::AppendNewProcess(KProcess* process) {
890 impl->process_list.push_back(process); 872 impl->process_list.push_back(process);
891} 873}
@@ -959,14 +941,6 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
959 return *impl->hardware_timer; 941 return *impl->hardware_timer;
960} 942}
961 943
962Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() {
963 return *impl->exclusive_monitor;
964}
965
966const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
967 return *impl->exclusive_monitor;
968}
969
970KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 944KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
971 return *impl->global_object_list_container; 945 return *impl->global_object_list_container;
972} 946}
@@ -1030,14 +1004,6 @@ u64 KernelCore::CreateNewUserProcessID() {
1030 return impl->next_user_process_id++; 1004 return impl->next_user_process_id++;
1031} 1005}
1032 1006
1033KHandleTable& KernelCore::GlobalHandleTable() {
1034 return *impl->global_handle_table;
1035}
1036
1037const KHandleTable& KernelCore::GlobalHandleTable() const {
1038 return *impl->global_handle_table;
1039}
1040
1041void KernelCore::RegisterCoreThread(std::size_t core_id) { 1007void KernelCore::RegisterCoreThread(std::size_t core_id) {
1042 impl->RegisterCoreThread(core_id); 1008 impl->RegisterCoreThread(core_id);
1043} 1009}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 78c88902c..5d4102145 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -116,9 +116,6 @@ public:
116 /// Retrieves a shared pointer to the system resource limit instance. 116 /// Retrieves a shared pointer to the system resource limit instance.
117 KResourceLimit* GetSystemResourceLimit(); 117 KResourceLimit* GetSystemResourceLimit();
118 118
119 /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
120 KScopedAutoObject<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const;
121
122 /// Adds the given shared pointer to an internal list of active processes. 119 /// Adds the given shared pointer to an internal list of active processes.
123 void AppendNewProcess(KProcess* process); 120 void AppendNewProcess(KProcess* process);
124 121
@@ -170,10 +167,6 @@ public:
170 /// Stops execution of 'id' core, in order to reschedule a new thread. 167 /// Stops execution of 'id' core, in order to reschedule a new thread.
171 void PrepareReschedule(std::size_t id); 168 void PrepareReschedule(std::size_t id);
172 169
173 Core::ExclusiveMonitor& GetExclusiveMonitor();
174
175 const Core::ExclusiveMonitor& GetExclusiveMonitor() const;
176
177 KAutoObjectWithListContainer& ObjectListContainer(); 170 KAutoObjectWithListContainer& ObjectListContainer();
178 171
179 const KAutoObjectWithListContainer& ObjectListContainer() const; 172 const KAutoObjectWithListContainer& ObjectListContainer() const;
diff --git a/src/core/hle/kernel/message_buffer.h b/src/core/hle/kernel/message_buffer.h
index 75b275310..d528a9bb3 100644
--- a/src/core/hle/kernel/message_buffer.h
+++ b/src/core/hle/kernel/message_buffer.h
@@ -18,13 +18,13 @@ public:
18 static constexpr inline u64 NullTag = 0; 18 static constexpr inline u64 NullTag = 0;
19 19
20 public: 20 public:
21 enum class ReceiveListCountType : u32 { 21 enum ReceiveListCountType : u32 {
22 None = 0, 22 ReceiveListCountType_None = 0,
23 ToMessageBuffer = 1, 23 ReceiveListCountType_ToMessageBuffer = 1,
24 ToSingleBuffer = 2, 24 ReceiveListCountType_ToSingleBuffer = 2,
25 25
26 CountOffset = 2, 26 ReceiveListCountType_CountOffset = 2,
27 CountMax = 13, 27 ReceiveListCountType_CountMax = 13,
28 }; 28 };
29 29
30 private: 30 private:
@@ -591,16 +591,16 @@ public:
591 // Add the size of the receive list. 591 // Add the size of the receive list.
592 const auto count = hdr.GetReceiveListCount(); 592 const auto count = hdr.GetReceiveListCount();
593 switch (count) { 593 switch (count) {
594 case MessageHeader::ReceiveListCountType::None: 594 case MessageHeader::ReceiveListCountType_None:
595 break; 595 break;
596 case MessageHeader::ReceiveListCountType::ToMessageBuffer: 596 case MessageHeader::ReceiveListCountType_ToMessageBuffer:
597 break; 597 break;
598 case MessageHeader::ReceiveListCountType::ToSingleBuffer: 598 case MessageHeader::ReceiveListCountType_ToSingleBuffer:
599 msg_size += ReceiveListEntry::GetDataSize(); 599 msg_size += ReceiveListEntry::GetDataSize();
600 break; 600 break;
601 default: 601 default:
602 msg_size += (static_cast<s32>(count) - 602 msg_size += (static_cast<s32>(count) -
603 static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) * 603 static_cast<s32>(MessageHeader::ReceiveListCountType_CountOffset)) *
604 ReceiveListEntry::GetDataSize(); 604 ReceiveListEntry::GetDataSize();
605 break; 605 break;
606 } 606 }
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
index ada998772..231e4d0e1 100644
--- a/src/core/hle/kernel/svc/svc_info.cpp
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -118,7 +118,6 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
118 R_SUCCEED(); 118 R_SUCCEED();
119 119
120 case InfoType::IsApplication: 120 case InfoType::IsApplication:
121 LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
122 *result = process->IsApplication(); 121 *result = process->IsApplication();
123 R_SUCCEED(); 122 R_SUCCEED();
124 123
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 47a3e7bb0..85cc4f561 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -48,8 +48,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
48 }; 48 };
49 49
50 // Send the reply. 50 // Send the reply.
51 R_TRY(session->SendReply()); 51 R_TRY(session->SendReply(message, buffer_size, message_paddr));
52 // R_TRY(session->SendReply(message, buffer_size, message_paddr));
53 } 52 }
54 53
55 // Receive a message. 54 // Receive a message.
@@ -85,8 +84,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
85 if (R_SUCCEEDED(result)) { 84 if (R_SUCCEEDED(result)) {
86 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); 85 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
87 if (session != nullptr) { 86 if (session != nullptr) {
88 // result = session->ReceiveRequest(message, buffer_size, message_paddr); 87 result = session->ReceiveRequest(message, buffer_size, message_paddr);
89 result = session->ReceiveRequest();
90 if (ResultNotFound == result) { 88 if (ResultNotFound == result) {
91 continue; 89 continue;
92 } 90 }
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index e1ad78607..38e71d516 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -38,7 +38,9 @@ constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
38constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126}; 38constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
39constexpr Result ResultPortClosed{ErrorModule::Kernel, 131}; 39constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
40constexpr Result ResultLimitReached{ErrorModule::Kernel, 132}; 40constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
41constexpr Result ResultReceiveListBroken{ErrorModule::Kernel, 258};
41constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259}; 42constexpr Result ResultOutOfAddressSpace{ErrorModule::Kernel, 259};
43constexpr Result ResultMessageTooLarge{ErrorModule::Kernel, 260};
42constexpr Result ResultInvalidId{ErrorModule::Kernel, 519}; 44constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
43 45
44} // namespace Kernel 46} // namespace Kernel
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index a266d7c21..97eb56ff0 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1513,8 +1513,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
1513 return; 1513 return;
1514 } 1514 }
1515 1515
1516 auto transfer_mem = 1516 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1517 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
1518 1517
1519 if (transfer_mem.IsNull()) { 1518 if (transfer_mem.IsNull()) {
1520 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); 1519 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1524,8 +1523,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(HLERequestContext& ctx)
1524 } 1523 }
1525 1524
1526 std::vector<u8> memory(transfer_mem->GetSize()); 1525 std::vector<u8> memory(transfer_mem->GetSize());
1527 system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), 1526 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1528 memory.size());
1529 1527
1530 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1528 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1531 rb.Push(ResultSuccess); 1529 rb.Push(ResultSuccess);
@@ -1547,8 +1545,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
1547 return; 1545 return;
1548 } 1546 }
1549 1547
1550 auto transfer_mem = 1548 auto transfer_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(handle);
1551 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(handle);
1552 1549
1553 if (transfer_mem.IsNull()) { 1550 if (transfer_mem.IsNull()) {
1554 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle); 1551 LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
@@ -1558,8 +1555,7 @@ void ILibraryAppletCreator::CreateHandleStorage(HLERequestContext& ctx) {
1558 } 1555 }
1559 1556
1560 std::vector<u8> memory(transfer_mem->GetSize()); 1557 std::vector<u8> memory(transfer_mem->GetSize());
1561 system.ApplicationMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), 1558 ctx.GetMemory().ReadBlock(transfer_mem->GetSourceAddress(), memory.data(), memory.size());
1562 memory.size());
1563 1559
1564 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1560 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1565 rb.Push(ResultSuccess); 1561 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 23e56c77a..bd4ca753b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -454,10 +454,8 @@ void AudRenU::OpenAudioRenderer(HLERequestContext& ctx) {
454 return; 454 return;
455 } 455 }
456 456
457 const auto& handle_table{system.ApplicationProcess()->GetHandleTable()}; 457 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
458 auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)}; 458 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
459 auto transfer_memory{
460 process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
461 459
462 const auto session_id{impl->GetSessionId()}; 460 const auto session_id{impl->GetSessionId()};
463 if (session_id == -1) { 461 if (session_id == -1) {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 6a7bf9416..91f33aabd 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -278,9 +278,7 @@ void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) {
278 auto params = rp.PopRaw<OpusParameters>(); 278 auto params = rp.PopRaw<OpusParameters>();
279 auto transfer_memory_size{rp.Pop<u32>()}; 279 auto transfer_memory_size{rp.Pop<u32>()};
280 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 280 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
281 auto transfer_memory{ 281 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
282 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
283 transfer_memory_handle)};
284 282
285 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", 283 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
286 params.sample_rate, params.channel_count, transfer_memory_size); 284 params.sample_rate, params.channel_count, transfer_memory_size);
@@ -323,9 +321,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
323 321
324 auto transfer_memory_size{rp.Pop<u32>()}; 322 auto transfer_memory_size{rp.Pop<u32>()};
325 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 323 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
326 auto transfer_memory{ 324 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
327 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
328 transfer_memory_handle)};
329 325
330 LOG_DEBUG(Service_Audio, 326 LOG_DEBUG(Service_Audio,
331 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " 327 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
@@ -374,9 +370,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
374 auto params = rp.PopRaw<OpusParametersEx>(); 370 auto params = rp.PopRaw<OpusParametersEx>();
375 auto transfer_memory_size{rp.Pop<u32>()}; 371 auto transfer_memory_size{rp.Pop<u32>()};
376 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 372 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
377 auto transfer_memory{ 373 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
378 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
379 transfer_memory_handle)};
380 374
381 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", 375 LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}",
382 params.sample_rate, params.channel_count, transfer_memory_size); 376 params.sample_rate, params.channel_count, transfer_memory_size);
@@ -414,9 +408,7 @@ void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) {
414 408
415 auto transfer_memory_size{rp.Pop<u32>()}; 409 auto transfer_memory_size{rp.Pop<u32>()};
416 auto transfer_memory_handle{ctx.GetCopyHandle(0)}; 410 auto transfer_memory_handle{ctx.GetCopyHandle(0)};
417 auto transfer_memory{ 411 auto transfer_memory{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(transfer_memory_handle)};
418 system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
419 transfer_memory_handle)};
420 412
421 LOG_DEBUG(Service_Audio, 413 LOG_DEBUG(Service_Audio,
422 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " 414 "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} "
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index fe2ed8df8..31da86074 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -89,7 +89,7 @@ static void GenerateErrorReport(Core::System& system, Result error_code, const F
89 crash_report += fmt::format(" ESR: {:016x}\n", info.esr); 89 crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
90 crash_report += fmt::format(" FAR: {:016x}\n", info.far); 90 crash_report += fmt::format(" FAR: {:016x}\n", info.far);
91 crash_report += "\nBacktrace:\n"; 91 crash_report += "\nBacktrace:\n";
92 for (size_t i = 0; i < info.backtrace_size; i++) { 92 for (u32 i = 0; i < std::min<u32>(info.backtrace_size, 32); i++) {
93 crash_report += 93 crash_report +=
94 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); 94 fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
95 } 95 }
diff --git a/src/core/hle/service/hid/controllers/applet_resource.cpp b/src/core/hle/service/hid/controllers/applet_resource.cpp
index c8e74c764..b4ff663c2 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.cpp
+++ b/src/core/hle/service/hid/controllers/applet_resource.cpp
@@ -4,7 +4,7 @@
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h" 5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/applet_resource.h" 6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/controllers/shared_memory_format.h" 7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8#include "core/hle/service/hid/errors.h" 8#include "core/hle/service/hid/errors.h"
9 9
10namespace Service::HID { 10namespace Service::HID {
@@ -164,6 +164,22 @@ Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_mem
164 return ResultSuccess; 164 return ResultSuccess;
165} 165}
166 166
167AruidData* AppletResource::GetAruidData(u64 aruid) {
168 const u64 aruid_index = GetIndexFromAruid(aruid);
169 if (aruid_index == AruidIndexMax) {
170 return nullptr;
171 }
172 return &data[aruid_index];
173}
174
175AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) {
176 return &data[aruid_index];
177}
178
179bool AppletResource::IsVibrationAruidActive(u64 aruid) const {
180 return aruid == 0 || aruid == active_vibration_aruid;
181}
182
167u64 AppletResource::GetIndexFromAruid(u64 aruid) { 183u64 AppletResource::GetIndexFromAruid(u64 aruid) {
168 for (std::size_t i = 0; i < AruidIndexMax; i++) { 184 for (std::size_t i = 0; i < AruidIndexMax; i++) {
169 if (registration_list.flag[i] == RegistrationStatus::Initialized && 185 if (registration_list.flag[i] == RegistrationStatus::Initialized &&
diff --git a/src/core/hle/service/hid/controllers/applet_resource.h b/src/core/hle/service/hid/controllers/applet_resource.h
index e7991f93a..0862fdc2f 100644
--- a/src/core/hle/service/hid/controllers/applet_resource.h
+++ b/src/core/hle/service/hid/controllers/applet_resource.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <mutex>
7 8
8#include "common/bit_field.h" 9#include "common/bit_field.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -20,6 +21,60 @@ class KSharedMemory;
20 21
21namespace Service::HID { 22namespace Service::HID {
22struct SharedMemoryFormat; 23struct SharedMemoryFormat;
24class AppletResource;
25class NPadResource;
26
27static constexpr std::size_t AruidIndexMax = 0x20;
28static constexpr u64 SystemAruid = 0;
29
30enum class RegistrationStatus : u32 {
31 None,
32 Initialized,
33 PendingDelete,
34};
35
36struct DataStatusFlag {
37 union {
38 u32 raw{};
39
40 BitField<0, 1, u32> is_initialized;
41 BitField<1, 1, u32> is_assigned;
42 BitField<16, 1, u32> enable_pad_input;
43 BitField<17, 1, u32> enable_six_axis_sensor;
44 BitField<18, 1, u32> bit_18;
45 BitField<19, 1, u32> is_palma_connectable;
46 BitField<20, 1, u32> enable_palma_boost_mode;
47 BitField<21, 1, u32> enable_touchscreen;
48 };
49};
50
51struct AruidRegisterList {
52 std::array<RegistrationStatus, AruidIndexMax> flag{};
53 std::array<u64, AruidIndexMax> aruid{};
54};
55static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
56
57struct AruidData {
58 DataStatusFlag flag{};
59 u64 aruid{};
60 SharedMemoryFormat* shared_memory_format{nullptr};
61};
62
63struct HandheldConfig {
64 bool is_handheld_hid_enabled;
65 bool is_force_handheld;
66 bool is_joycon_rail_enabled;
67 bool is_force_handheld_style_vibration;
68};
69static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size");
70
71struct AppletResourceHolder {
72 std::shared_ptr<AppletResource> applet_resource{nullptr};
73 std::recursive_mutex* shared_mutex{nullptr};
74 NPadResource* shared_npad_resource{nullptr};
75 std::shared_ptr<HandheldConfig> handheld_config{nullptr};
76 long* handle_1;
77};
23 78
24class AppletResource { 79class AppletResource {
25public: 80public:
@@ -36,6 +91,10 @@ public:
36 u64 GetActiveAruid(); 91 u64 GetActiveAruid();
37 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid); 92 Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
38 Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid); 93 Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
94 AruidData* GetAruidData(u64 aruid);
95 AruidData* GetAruidDataByIndex(std::size_t aruid_index);
96
97 bool IsVibrationAruidActive(u64 aruid) const;
39 98
40 u64 GetIndexFromAruid(u64 aruid); 99 u64 GetIndexFromAruid(u64 aruid);
41 100
@@ -52,46 +111,12 @@ public:
52 Result UnregisterCoreAppletResource(); 111 Result UnregisterCoreAppletResource();
53 112
54private: 113private:
55 static constexpr std::size_t AruidIndexMax = 0x20;
56
57 enum RegistrationStatus : u32 {
58 None,
59 Initialized,
60 PendingDelete,
61 };
62
63 struct DataStatusFlag {
64 union {
65 u32 raw{};
66
67 BitField<0, 1, u32> is_initialized;
68 BitField<1, 1, u32> is_assigned;
69 BitField<16, 1, u32> enable_pad_input;
70 BitField<17, 1, u32> enable_six_axis_sensor;
71 BitField<18, 1, u32> bit_18;
72 BitField<19, 1, u32> is_palma_connectable;
73 BitField<20, 1, u32> enable_palma_boost_mode;
74 BitField<21, 1, u32> enable_touchscreen;
75 };
76 };
77
78 struct AruidRegisterList {
79 std::array<RegistrationStatus, AruidIndexMax> flag{};
80 std::array<u64, AruidIndexMax> aruid{};
81 };
82 static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");
83
84 struct AruidData {
85 DataStatusFlag flag{};
86 u64 aruid{};
87 SharedMemoryFormat* shared_memory_format{nullptr};
88 };
89
90 u64 active_aruid{}; 114 u64 active_aruid{};
91 AruidRegisterList registration_list{}; 115 AruidRegisterList registration_list{};
92 std::array<AruidData, AruidIndexMax> data{}; 116 std::array<AruidData, AruidIndexMax> data{};
93 std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{}; 117 std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
94 s32 ref_counter{}; 118 s32 ref_counter{};
119 u64 active_vibration_aruid;
95 120
96 Core::System& system; 121 Core::System& system;
97}; 122};
diff --git a/src/core/hle/service/hid/controllers/capture_button.cpp b/src/core/hle/service/hid/controllers/capture_button.cpp
new file mode 100644
index 000000000..7847c080e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/capture_button.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/capture_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11CaptureButton::CaptureButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13CaptureButton::~CaptureButton() = default;
14
15void CaptureButton::OnInit() {}
16
17void CaptureButton::OnRelease() {}
18
19void CaptureButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 auto& header = data->shared_memory_format->capture_button.header;
33 header.timestamp = core_timing.GetGlobalTimeNs().count();
34 header.total_entry_count = 17;
35 header.entry_count = 0;
36 header.last_entry_index = 0;
37}
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/capture_button.h
index d2052fb17..dcc4715c5 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/capture_button.h
@@ -6,12 +6,11 @@
6#include "core/hle/service/hid/controllers/controller_base.h" 6#include "core/hle/service/hid/controllers/controller_base.h"
7 7
8namespace Service::HID { 8namespace Service::HID {
9struct CommonHeader;
10 9
11class Controller_Stubbed final : public ControllerBase { 10class CaptureButton final : public ControllerBase {
12public: 11public:
13 explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, CommonHeader& ring_lifo_header); 12 explicit CaptureButton(Core::HID::HIDCore& hid_core_);
14 ~Controller_Stubbed() override; 13 ~CaptureButton() override;
15 14
16 // Called when the controller is initialized 15 // Called when the controller is initialized
17 void OnInit() override; 16 void OnInit() override;
@@ -23,7 +22,6 @@ public:
23 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
24 23
25private: 24private:
26 CommonHeader& header;
27 bool smart_update{}; 25 bool smart_update{};
28}; 26};
29} // namespace Service::HID 27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.cpp b/src/core/hle/service/hid/controllers/console_six_axis.cpp
index 3961d2b5f..4b574c2e5 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/console_six_axis.cpp
@@ -5,13 +5,11 @@
5#include "core/hid/emulated_console.h" 5#include "core/hid/emulated_console.h"
6#include "core/hid/hid_core.h" 6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/controllers/console_six_axis.h" 7#include "core/hle/service/hid/controllers/console_six_axis.h"
8#include "core/hle/service/hid/controllers/shared_memory_format.h" 8#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
9 9
10namespace Service::HID { 10namespace Service::HID {
11 11
12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_, 12ConsoleSixAxis::ConsoleSixAxis(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
13 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory)
14 : ControllerBase{hid_core_}, shared_memory{console_shared_memory} {
15 console = hid_core.GetEmulatedConsole(); 13 console = hid_core.GetEmulatedConsole();
16} 14}
17 15
@@ -22,6 +20,16 @@ void ConsoleSixAxis::OnInit() {}
22void ConsoleSixAxis::OnRelease() {} 20void ConsoleSixAxis::OnRelease() {}
23 21
24void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 22void ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
23 std::scoped_lock shared_lock{*shared_mutex};
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr || !data->flag.is_assigned) {
28 return;
29 }
30
31 ConsoleSixAxisSensorSharedMemoryFormat& shared_memory = data->shared_memory_format->console;
32
25 if (!IsControllerActivated()) { 33 if (!IsControllerActivated()) {
26 return; 34 return;
27 } 35 }
diff --git a/src/core/hle/service/hid/controllers/console_six_axis.h b/src/core/hle/service/hid/controllers/console_six_axis.h
index 3d1c9ce23..e3351f83c 100644
--- a/src/core/hle/service/hid/controllers/console_six_axis.h
+++ b/src/core/hle/service/hid/controllers/console_six_axis.h
@@ -10,12 +10,9 @@ class EmulatedConsole;
10} // namespace Core::HID 10} // namespace Core::HID
11 11
12namespace Service::HID { 12namespace Service::HID {
13struct ConsoleSixAxisSensorSharedMemoryFormat;
14
15class ConsoleSixAxis final : public ControllerBase { 13class ConsoleSixAxis final : public ControllerBase {
16public: 14public:
17 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_, 15 explicit ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
18 ConsoleSixAxisSensorSharedMemoryFormat& console_shared_memory);
19 ~ConsoleSixAxis() override; 16 ~ConsoleSixAxis() override;
20 17
21 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -28,7 +25,6 @@ public:
28 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 25 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
29 26
30private: 27private:
31 ConsoleSixAxisSensorSharedMemoryFormat& shared_memory;
32 Core::HID::EmulatedConsole* console = nullptr; 28 Core::HID::EmulatedConsole* console = nullptr;
33}; 29};
34} // namespace Service::HID 30} // 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 0bcd87062..afca7154c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -31,4 +31,11 @@ void ControllerBase::DeactivateController() {
31bool ControllerBase::IsControllerActivated() const { 31bool ControllerBase::IsControllerActivated() const {
32 return is_activated; 32 return is_activated;
33} 33}
34
35void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource,
36 std::recursive_mutex* resource_mutex) {
37 applet_resource = resource;
38 shared_mutex = resource_mutex;
39}
40
34} // namespace Service::HID 41} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 4326c7821..b34b85ece 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -3,8 +3,11 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <memory>
7
6#include "common/common_types.h" 8#include "common/common_types.h"
7#include "core/hle/result.h" 9#include "core/hle/result.h"
10#include "core/hle/service/hid/controllers/applet_resource.h"
8 11
9namespace Core::Timing { 12namespace Core::Timing {
10class CoreTiming; 13class CoreTiming;
@@ -12,7 +15,7 @@ class CoreTiming;
12 15
13namespace Core::HID { 16namespace Core::HID {
14class HIDCore; 17class HIDCore;
15} 18} // namespace Core::HID
16 19
17namespace Service::HID { 20namespace Service::HID {
18class ControllerBase { 21class ControllerBase {
@@ -39,8 +42,13 @@ public:
39 42
40 bool IsControllerActivated() const; 43 bool IsControllerActivated() const;
41 44
45 void SetAppletResource(std::shared_ptr<AppletResource> resource,
46 std::recursive_mutex* resource_mutex);
47
42protected: 48protected:
43 bool is_activated{false}; 49 bool is_activated{false};
50 std::shared_ptr<AppletResource> applet_resource{nullptr};
51 std::recursive_mutex* shared_mutex{nullptr};
44 52
45 Core::HID::HIDCore& hid_core; 53 Core::HID::HIDCore& hid_core;
46}; 54};
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.cpp b/src/core/hle/service/hid/controllers/debug_mouse.cpp
new file mode 100644
index 000000000..ceeb78d36
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.cpp
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/debug_mouse.h"
10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11
12namespace Service::HID {
13
14DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 emulated_devices = hid_core.GetEmulatedDevices();
16}
17
18DebugMouse::~DebugMouse() = default;
19
20void DebugMouse::OnInit() {}
21void DebugMouse::OnRelease() {}
22
23void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse;
33
34 if (!IsControllerActivated()) {
35 shared_memory.mouse_lifo.buffer_count = 0;
36 shared_memory.mouse_lifo.buffer_tail = 0;
37 return;
38 }
39
40 next_state = {};
41
42 const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
43 next_state.sampling_number = last_entry.sampling_number + 1;
44
45 if (Settings::values.mouse_enabled) {
46 const auto& mouse_button_state = emulated_devices->GetMouseButtons();
47 const auto& mouse_position_state = emulated_devices->GetMousePosition();
48 const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
49 next_state.attribute.is_connected.Assign(1);
50 next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
51 next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
52 next_state.delta_x = next_state.x - last_entry.x;
53 next_state.delta_y = next_state.y - last_entry.y;
54 next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
55 next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;
56
57 last_mouse_wheel_state = mouse_wheel_state;
58 next_state.button = mouse_button_state;
59 }
60
61 shared_memory.mouse_lifo.WriteNextEntry(next_state);
62}
63
64} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_mouse.h b/src/core/hle/service/hid/controllers/debug_mouse.h
new file mode 100644
index 000000000..ec939fa9f
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/debug_mouse.h
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Core::HID {
9class EmulatedDevices;
10struct MouseState;
11struct AnalogStickState;
12} // namespace Core::HID
13
14namespace Service::HID {
15class DebugMouse final : public ControllerBase {
16public:
17 explicit DebugMouse(Core::HID::HIDCore& hid_core_);
18 ~DebugMouse() override;
19
20 // Called when the controller is initialized
21 void OnInit() override;
22
23 // When the controller is released
24 void OnRelease() override;
25
26 // When the controller is requesting an update for the shared memory
27 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
28
29private:
30 Core::HID::MouseState next_state{};
31 Core::HID::AnalogStickState last_mouse_wheel_state{};
32 Core::HID::EmulatedDevices* emulated_devices = nullptr;
33};
34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 7d2370b4f..dc83f90f3 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,14 +6,13 @@
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 "core/hid/hid_types.h" 8#include "core/hid/hid_types.h"
9#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/debug_pad.h" 10#include "core/hle/service/hid/controllers/debug_pad.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
13 14
14DebugPad::DebugPad(Core::HID::HIDCore& hid_core_, 15DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
15 DebugPadSharedMemoryFormat& debug_pad_shared_memory)
16 : ControllerBase{hid_core_}, shared_memory{debug_pad_shared_memory} {
17 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other); 16 controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
18} 17}
19 18
@@ -24,6 +23,16 @@ void DebugPad::OnInit() {}
24void DebugPad::OnRelease() {} 23void DebugPad::OnRelease() {}
25 24
26void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 25void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
26 std::scoped_lock shared_lock{*shared_mutex};
27 const u64 aruid = applet_resource->GetActiveAruid();
28 auto* data = applet_resource->GetAruidData(aruid);
29
30 if (data == nullptr || !data->flag.is_assigned) {
31 return;
32 }
33
34 DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad;
35
27 if (!IsControllerActivated()) { 36 if (!IsControllerActivated()) {
28 shared_memory.debug_pad_lifo.buffer_count = 0; 37 shared_memory.debug_pad_lifo.buffer_count = 0;
29 shared_memory.debug_pad_lifo.buffer_tail = 0; 38 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 8ab29eca8..dd00b2402 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -15,12 +15,9 @@ class CoreTiming;
15} 15}
16 16
17namespace Service::HID { 17namespace Service::HID {
18struct DebugPadSharedMemoryFormat;
19
20class DebugPad final : public ControllerBase { 18class DebugPad final : public ControllerBase {
21public: 19public:
22 explicit DebugPad(Core::HID::HIDCore& hid_core_, 20 explicit DebugPad(Core::HID::HIDCore& hid_core_);
23 DebugPadSharedMemoryFormat& debug_pad_shared_memory);
24 ~DebugPad() override; 21 ~DebugPad() override;
25 22
26 // Called when the controller is initialized 23 // Called when the controller is initialized
@@ -34,7 +31,6 @@ public:
34 31
35private: 32private:
36 DebugPadState next_state{}; 33 DebugPadState next_state{};
37 DebugPadSharedMemoryFormat& shared_memory;
38 Core::HID::EmulatedController* controller = nullptr; 34 Core::HID::EmulatedController* controller = nullptr;
39}; 35};
40} // namespace Service::HID 36} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.cpp b/src/core/hle/service/hid/controllers/digitizer.cpp
new file mode 100644
index 000000000..d5514c965
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/digitizer.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13Digitizer::~Digitizer() = default;
14
15void Digitizer::OnInit() {}
16
17void Digitizer::OnRelease() {}
18
19void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 auto& header = data->shared_memory_format->digitizer.header;
33 header.timestamp = core_timing.GetGlobalTimeNs().count();
34 header.total_entry_count = 17;
35 header.entry_count = 0;
36 header.last_entry_index = 0;
37}
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/digitizer.h b/src/core/hle/service/hid/controllers/digitizer.h
new file mode 100644
index 000000000..d81f814c3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/digitizer.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class Digitizer final : public ControllerBase {
11public:
12 explicit Digitizer(Core::HID::HIDCore& hid_core_);
13 ~Digitizer() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index f658005f6..c73da13ee 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -6,8 +6,9 @@
6#include "core/frontend/emu_window.h" 6#include "core/frontend/emu_window.h"
7#include "core/hid/emulated_console.h" 7#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h" 8#include "core/hid/hid_core.h"
9#include "core/hle/service/hid/controllers/applet_resource.h"
9#include "core/hle/service/hid/controllers/gesture.h" 10#include "core/hle/service/hid/controllers/gesture.h"
10#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11 12
12namespace Service::HID { 13namespace Service::HID {
13// HW is around 700, value is set to 400 to make it easier to trigger with mouse 14// HW is around 700, value is set to 400 to make it easier to trigger with mouse
@@ -21,24 +22,42 @@ constexpr f32 Square(s32 num) {
21 return static_cast<f32>(num * num); 22 return static_cast<f32>(num * num);
22} 23}
23 24
24Gesture::Gesture(Core::HID::HIDCore& hid_core_, GestureSharedMemoryFormat& gesture_shared_memory) 25Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
25 : ControllerBase(hid_core_), shared_memory{gesture_shared_memory} {
26 console = hid_core.GetEmulatedConsole(); 26 console = hid_core.GetEmulatedConsole();
27} 27}
28Gesture::~Gesture() = default; 28Gesture::~Gesture() = default;
29 29
30void Gesture::OnInit() { 30void Gesture::OnInit() {
31 shared_memory.gesture_lifo.buffer_count = 0; 31 std::scoped_lock shared_lock{*shared_mutex};
32 shared_memory.gesture_lifo.buffer_tail = 0; 32 const u64 aruid = applet_resource->GetActiveAruid();
33 auto* data = applet_resource->GetAruidData(aruid);
34
35 if (data == nullptr || !data->flag.is_assigned) {
36 return;
37 }
38
39 shared_memory = &data->shared_memory_format->gesture;
40 shared_memory->gesture_lifo.buffer_count = 0;
41 shared_memory->gesture_lifo.buffer_tail = 0;
33 force_update = true; 42 force_update = true;
34} 43}
35 44
36void Gesture::OnRelease() {} 45void Gesture::OnRelease() {}
37 46
38void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 47void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
48 std::scoped_lock shared_lock{*shared_mutex};
49 const u64 aruid = applet_resource->GetActiveAruid();
50 auto* data = applet_resource->GetAruidData(aruid);
51
52 if (data == nullptr || !data->flag.is_assigned) {
53 return;
54 }
55
56 shared_memory = &data->shared_memory_format->gesture;
57
39 if (!IsControllerActivated()) { 58 if (!IsControllerActivated()) {
40 shared_memory.gesture_lifo.buffer_count = 0; 59 shared_memory->gesture_lifo.buffer_count = 0;
41 shared_memory.gesture_lifo.buffer_tail = 0; 60 shared_memory->gesture_lifo.buffer_tail = 0;
42 return; 61 return;
43 } 62 }
44 63
@@ -46,7 +65,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
46 65
47 GestureProperties gesture = GetGestureProperties(); 66 GestureProperties gesture = GetGestureProperties();
48 f32 time_difference = 67 f32 time_difference =
49 static_cast<f32>(shared_memory.gesture_lifo.timestamp - last_update_timestamp) / 68 static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
50 (1000 * 1000 * 1000); 69 (1000 * 1000 * 1000);
51 70
52 // Only update if necessary 71 // Only update if necessary
@@ -54,7 +73,7 @@ void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
54 return; 73 return;
55 } 74 }
56 75
57 last_update_timestamp = shared_memory.gesture_lifo.timestamp; 76 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
58 UpdateGestureSharedMemory(gesture, time_difference); 77 UpdateGestureSharedMemory(gesture, time_difference);
59} 78}
60 79
@@ -97,7 +116,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
97 GestureType type = GestureType::Idle; 116 GestureType type = GestureType::Idle;
98 GestureAttribute attributes{}; 117 GestureAttribute attributes{};
99 118
100 const auto& last_entry = shared_memory.gesture_lifo.ReadCurrentEntry().state; 119 const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
101 120
102 // Reset next state to default 121 // Reset next state to default
103 next_state.sampling_number = last_entry.sampling_number + 1; 122 next_state.sampling_number = last_entry.sampling_number + 1;
@@ -127,7 +146,7 @@ void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_dif
127 next_state.points = gesture.points; 146 next_state.points = gesture.points;
128 last_gesture = gesture; 147 last_gesture = gesture;
129 148
130 shared_memory.gesture_lifo.WriteNextEntry(next_state); 149 shared_memory->gesture_lifo.WriteNextEntry(next_state);
131} 150}
132 151
133void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, 152void Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -300,7 +319,7 @@ void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_
300} 319}
301 320
302const GestureState& Gesture::GetLastGestureEntry() const { 321const GestureState& Gesture::GetLastGestureEntry() const {
303 return shared_memory.gesture_lifo.ReadCurrentEntry().state; 322 return shared_memory->gesture_lifo.ReadCurrentEntry().state;
304} 323}
305 324
306GestureProperties Gesture::GetGestureProperties() { 325GestureProperties Gesture::GetGestureProperties() {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 41fdfcd03..78da1552a 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -18,8 +18,7 @@ struct GestureSharedMemoryFormat;
18 18
19class Gesture final : public ControllerBase { 19class Gesture final : public ControllerBase {
20public: 20public:
21 explicit Gesture(Core::HID::HIDCore& hid_core_, 21 explicit Gesture(Core::HID::HIDCore& hid_core_);
22 GestureSharedMemoryFormat& gesture_shared_memory);
23 ~Gesture() override; 22 ~Gesture() override;
24 23
25 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -74,7 +73,7 @@ private:
74 GestureProperties GetGestureProperties(); 73 GestureProperties GetGestureProperties();
75 74
76 GestureState next_state{}; 75 GestureState next_state{};
77 GestureSharedMemoryFormat& shared_memory; 76 GestureSharedMemoryFormat* shared_memory;
78 Core::HID::EmulatedConsole* console = nullptr; 77 Core::HID::EmulatedConsole* console = nullptr;
79 78
80 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; 79 std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/home_button.cpp b/src/core/hle/service/hid/controllers/home_button.cpp
new file mode 100644
index 000000000..1397379f3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/home_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11HomeButton::HomeButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13HomeButton::~HomeButton() = default;
14
15void HomeButton::OnInit() {}
16
17void HomeButton::OnRelease() {}
18
19void HomeButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 auto& header = data->shared_memory_format->home_button.header;
33 header.timestamp = core_timing.GetGlobalTimeNs().count();
34 header.total_entry_count = 17;
35 header.entry_count = 0;
36 header.last_entry_index = 0;
37}
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/home_button.h b/src/core/hle/service/hid/controllers/home_button.h
new file mode 100644
index 000000000..e91c2aa5d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/home_button.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class HomeButton final : public ControllerBase {
11public:
12 explicit HomeButton(Core::HID::HIDCore& hid_core_);
13 ~HomeButton() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 871e5036a..c069bcbb2 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -5,14 +5,13 @@
5#include "core/core_timing.h" 5#include "core/core_timing.h"
6#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
8#include "core/hle/service/hid/controllers/keyboard.h" 9#include "core/hle/service/hid/controllers/keyboard.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h" 10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
10 11
11namespace Service::HID { 12namespace Service::HID {
12 13
13Keyboard::Keyboard(Core::HID::HIDCore& hid_core_, 14Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
14 KeyboardSharedMemoryFormat& keyboard_shared_memory)
15 : ControllerBase{hid_core_}, shared_memory{keyboard_shared_memory} {
16 emulated_devices = hid_core.GetEmulatedDevices(); 15 emulated_devices = hid_core.GetEmulatedDevices();
17} 16}
18 17
@@ -23,6 +22,16 @@ void Keyboard::OnInit() {}
23void Keyboard::OnRelease() {} 22void Keyboard::OnRelease() {}
24 23
25void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 24void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
25 std::scoped_lock shared_lock{*shared_mutex};
26 const u64 aruid = applet_resource->GetActiveAruid();
27 auto* data = applet_resource->GetAruidData(aruid);
28
29 if (data == nullptr || !data->flag.is_assigned) {
30 return;
31 }
32
33 KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard;
34
26 if (!IsControllerActivated()) { 35 if (!IsControllerActivated()) {
27 shared_memory.keyboard_lifo.buffer_count = 0; 36 shared_memory.keyboard_lifo.buffer_count = 0;
28 shared_memory.keyboard_lifo.buffer_tail = 0; 37 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 4d72171b9..e8ca326c6 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -7,12 +7,9 @@
7#include "core/hle/service/hid/controllers/types/keyboard_types.h" 7#include "core/hle/service/hid/controllers/types/keyboard_types.h"
8 8
9namespace Service::HID { 9namespace Service::HID {
10struct KeyboardSharedMemoryFormat;
11
12class Keyboard final : public ControllerBase { 10class Keyboard final : public ControllerBase {
13public: 11public:
14 explicit Keyboard(Core::HID::HIDCore& hid_core_, 12 explicit Keyboard(Core::HID::HIDCore& hid_core_);
15 KeyboardSharedMemoryFormat& keyboard_shared_memory);
16 ~Keyboard() override; 13 ~Keyboard() override;
17 14
18 // Called when the controller is initialized 15 // Called when the controller is initialized
@@ -26,7 +23,6 @@ public:
26 23
27private: 24private:
28 KeyboardState next_state{}; 25 KeyboardState next_state{};
29 KeyboardSharedMemoryFormat& shared_memory;
30 Core::HID::EmulatedDevices* emulated_devices = nullptr; 26 Core::HID::EmulatedDevices* emulated_devices = nullptr;
31}; 27};
32} // namespace Service::HID 28} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index de5b2c804..3a8d1751b 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,13 +5,13 @@
5#include "core/frontend/emu_window.h" 5#include "core/frontend/emu_window.h"
6#include "core/hid/emulated_devices.h" 6#include "core/hid/emulated_devices.h"
7#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
8#include "core/hle/service/hid/controllers/applet_resource.h"
8#include "core/hle/service/hid/controllers/mouse.h" 9#include "core/hle/service/hid/controllers/mouse.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h" 10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
10 11
11namespace Service::HID { 12namespace Service::HID {
12 13
13Mouse::Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory) 14Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
14 : ControllerBase{hid_core_}, shared_memory{mouse_shared_memory} {
15 emulated_devices = hid_core.GetEmulatedDevices(); 15 emulated_devices = hid_core.GetEmulatedDevices();
16} 16}
17 17
@@ -21,6 +21,16 @@ void Mouse::OnInit() {}
21void Mouse::OnRelease() {} 21void Mouse::OnRelease() {}
22 22
23void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 23void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse;
33
24 if (!IsControllerActivated()) { 34 if (!IsControllerActivated()) {
25 shared_memory.mouse_lifo.buffer_count = 0; 35 shared_memory.mouse_lifo.buffer_count = 0;
26 shared_memory.mouse_lifo.buffer_tail = 0; 36 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 363f316a5..cefad956c 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -12,11 +12,9 @@ struct AnalogStickState;
12} // namespace Core::HID 12} // namespace Core::HID
13 13
14namespace Service::HID { 14namespace Service::HID {
15struct MouseSharedMemoryFormat;
16
17class Mouse final : public ControllerBase { 15class Mouse final : public ControllerBase {
18public: 16public:
19 explicit Mouse(Core::HID::HIDCore& hid_core_, MouseSharedMemoryFormat& mouse_shared_memory); 17 explicit Mouse(Core::HID::HIDCore& hid_core_);
20 ~Mouse() override; 18 ~Mouse() override;
21 19
22 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -31,7 +29,6 @@ public:
31private: 29private:
32 Core::HID::MouseState next_state{}; 30 Core::HID::MouseState next_state{};
33 Core::HID::AnalogStickState last_mouse_wheel_state{}; 31 Core::HID::AnalogStickState last_mouse_wheel_state{};
34 MouseSharedMemoryFormat& shared_memory;
35 Core::HID::EmulatedDevices* emulated_devices = nullptr; 32 Core::HID::EmulatedDevices* emulated_devices = nullptr;
36}; 33};
37} // namespace Service::HID 34} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 53a737cf5..17cd0d7a0 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -16,46 +16,102 @@
16#include "core/hid/hid_core.h" 16#include "core/hid/hid_core.h"
17#include "core/hle/kernel/k_event.h" 17#include "core/hle/kernel/k_event.h"
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/applet_resource.h"
19#include "core/hle/service/hid/controllers/npad.h" 20#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/controllers/shared_memory_format.h" 21#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
21#include "core/hle/service/hid/errors.h" 22#include "core/hle/service/hid/errors.h"
22#include "core/hle/service/hid/hid_util.h" 23#include "core/hle/service/hid/hid_util.h"
23#include "core/hle/service/kernel_helpers.h" 24#include "core/hle/service/kernel_helpers.h"
24 25
25namespace Service::HID { 26namespace Service::HID {
26constexpr std::array<Core::HID::NpadIdType, 10> npad_id_list{ 27
27 Core::HID::NpadIdType::Player1, Core::HID::NpadIdType::Player2, Core::HID::NpadIdType::Player3, 28NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
28 Core::HID::NpadIdType::Player4, Core::HID::NpadIdType::Player5, Core::HID::NpadIdType::Player6, 29 : hid_core{hid_core_}, service_context{service_context_}, npad_resource{service_context} {
29 Core::HID::NpadIdType::Player7, Core::HID::NpadIdType::Player8, Core::HID::NpadIdType::Other, 30 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
30 Core::HID::NpadIdType::Handheld, 31 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
31}; 32 auto& controller = controller_data[aruid_index][i];
32 33 controller.device = hid_core.GetEmulatedControllerByIndex(i);
33NPad::NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format, 34 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
34 KernelHelpers::ServiceContext& service_context_) 35 Core::HID::DEFAULT_VIBRATION_VALUE;
35 : ControllerBase{hid_core_}, service_context{service_context_} { 36 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex]
36 for (std::size_t i = 0; i < controller_data.size(); ++i) { 37 .latest_vibration_value = Core::HID::DEFAULT_VIBRATION_VALUE;
37 auto& controller = controller_data[i]; 38 Core::HID::ControllerUpdateCallback engine_callback{
38 controller.shared_memory = &npad_shared_memory_format.npad_entry[i].internal_state; 39 .on_change =
39 controller.device = hid_core.GetEmulatedControllerByIndex(i); 40 [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
40 controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value = 41 .is_npad_service = true,
41 Core::HID::DEFAULT_VIBRATION_VALUE; 42 };
42 controller.vibration[Core::HID::EmulatedDeviceIndex::RightIndex].latest_vibration_value = 43 controller.callback_key = controller.device->SetCallback(engine_callback);
43 Core::HID::DEFAULT_VIBRATION_VALUE; 44 }
44 Core::HID::ControllerUpdateCallback engine_callback{
45 .on_change = [this,
46 i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
47 .is_npad_service = true,
48 };
49 controller.callback_key = controller.device->SetCallback(engine_callback);
50 } 45 }
51} 46}
52 47
53NPad::~NPad() { 48NPad::~NPad() {
54 for (std::size_t i = 0; i < controller_data.size(); ++i) { 49 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
55 auto& controller = controller_data[i]; 50 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
56 controller.device->DeleteCallback(controller.callback_key); 51 auto& controller = controller_data[aruid_index][i];
52 controller.device->DeleteCallback(controller.callback_key);
53 }
54 }
55}
56
57Result NPad::Activate() {
58 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
59 return ResultNpadResourceOverflow;
60 }
61
62 if (ref_counter == 0) {
63 std::scoped_lock lock{mutex};
64
65 // TODO: Activate handlers and AbstractedPad
57 } 66 }
58 OnRelease(); 67
68 ref_counter++;
69 return ResultSuccess;
70}
71
72Result NPad::Activate(u64 aruid) {
73 std::scoped_lock lock{mutex};
74 std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex};
75
76 auto* data = applet_resource_holder.applet_resource->GetAruidData(aruid);
77 const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
78
79 if (data == nullptr || !data->flag.is_assigned) {
80 return ResultSuccess;
81 }
82
83 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
84 auto& controller = controller_data[aruid_index][i];
85 controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
86 }
87
88 // Prefill controller buffers
89 for (auto& controller : controller_data[aruid_index]) {
90 auto* npad = controller.shared_memory;
91 npad->fullkey_color = {
92 .attribute = ColorAttribute::NoController,
93 .fullkey = {},
94 };
95 npad->joycon_color = {
96 .attribute = ColorAttribute::NoController,
97 .left = {},
98 .right = {},
99 };
100 // HW seems to initialize the first 19 entries
101 for (std::size_t i = 0; i < 19; ++i) {
102 WriteEmptyEntry(npad);
103 }
104 }
105
106 return ResultSuccess;
107}
108
109Result NPad::ActivateNpadResource() {
110 return npad_resource.Activate();
111}
112
113Result NPad::ActivateNpadResource(u64 aruid) {
114 return npad_resource.Activate(aruid);
59} 115}
60 116
61void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) { 117void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
@@ -64,41 +120,50 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c
64 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx); 120 ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
65 return; 121 return;
66 } 122 }
67 if (controller_idx >= controller_data.size()) {
68 return;
69 }
70 123
71 auto& controller = controller_data[controller_idx]; 124 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
72 const auto is_connected = controller.device->IsConnected(); 125 if (controller_idx >= controller_data[aruid_index].size()) {
73 const auto npad_type = controller.device->GetNpadStyleIndex();
74 const auto npad_id = controller.device->GetNpadIdType();
75 switch (type) {
76 case Core::HID::ControllerTriggerType::Connected:
77 case Core::HID::ControllerTriggerType::Disconnected:
78 if (is_connected == controller.is_connected) {
79 return; 126 return;
80 } 127 }
81 UpdateControllerAt(npad_type, npad_id, is_connected); 128
82 break; 129 auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
83 case Core::HID::ControllerTriggerType::Battery: { 130
84 if (!controller.device->IsConnected()) { 131 if (!data->flag.is_assigned) {
85 return; 132 continue;
133 }
134
135 auto& controller = controller_data[aruid_index][controller_idx];
136 const auto is_connected = controller.device->IsConnected();
137 const auto npad_type = controller.device->GetNpadStyleIndex();
138 const auto npad_id = controller.device->GetNpadIdType();
139 switch (type) {
140 case Core::HID::ControllerTriggerType::Connected:
141 case Core::HID::ControllerTriggerType::Disconnected:
142 if (is_connected == controller.is_connected) {
143 return;
144 }
145 UpdateControllerAt(data->aruid, npad_type, npad_id, is_connected);
146 break;
147 case Core::HID::ControllerTriggerType::Battery: {
148 if (!controller.device->IsConnected()) {
149 return;
150 }
151 auto* shared_memory = controller.shared_memory;
152 const auto& battery_level = controller.device->GetBattery();
153 shared_memory->battery_level_dual = battery_level.dual.battery_level;
154 shared_memory->battery_level_left = battery_level.left.battery_level;
155 shared_memory->battery_level_right = battery_level.right.battery_level;
156 break;
157 }
158 default:
159 break;
86 } 160 }
87 auto* shared_memory = controller.shared_memory;
88 const auto& battery_level = controller.device->GetBattery();
89 shared_memory->battery_level_dual = battery_level.dual.battery_level;
90 shared_memory->battery_level_left = battery_level.left.battery_level;
91 shared_memory->battery_level_right = battery_level.right.battery_level;
92 break;
93 }
94 default:
95 break;
96 } 161 }
97} 162}
98 163
99void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { 164void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
100 auto& controller = GetControllerFromNpadIdType(npad_id); 165 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
101 if (!IsControllerSupported(controller.device->GetNpadStyleIndex())) { 166 if (!npad_resource.IsControllerSupported(aruid, controller.device->GetNpadStyleIndex())) {
102 return; 167 return;
103 } 168 }
104 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id); 169 LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
@@ -107,7 +172,7 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
107 const auto& battery_level = controller.device->GetBattery(); 172 const auto& battery_level = controller.device->GetBattery();
108 auto* shared_memory = controller.shared_memory; 173 auto* shared_memory = controller.shared_memory;
109 if (controller_type == Core::HID::NpadStyleIndex::None) { 174 if (controller_type == Core::HID::NpadStyleIndex::None) {
110 controller.styleset_changed_event->Signal(); 175 npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
111 return; 176 return;
112 } 177 }
113 178
@@ -291,45 +356,11 @@ void NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
291 Common::Input::PollingMode::Active); 356 Common::Input::PollingMode::Active);
292 } 357 }
293 358
294 SignalStyleSetChangedEvent(npad_id); 359 npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
295 WriteEmptyEntry(controller.shared_memory); 360 WriteEmptyEntry(controller.shared_memory);
296 hid_core.SetLastActiveController(npad_id); 361 hid_core.SetLastActiveController(npad_id);
297} 362}
298 363
299void NPad::OnInit() {
300 if (!IsControllerActivated()) {
301 return;
302 }
303
304 for (std::size_t i = 0; i < controller_data.size(); ++i) {
305 auto& controller = controller_data[i];
306 controller.styleset_changed_event =
307 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
308 }
309
310 supported_npad_id_types.resize(npad_id_list.size());
311 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
312 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
313
314 // Prefill controller buffers
315 for (auto& controller : controller_data) {
316 auto* npad = controller.shared_memory;
317 npad->fullkey_color = {
318 .attribute = ColorAttribute::NoController,
319 .fullkey = {},
320 };
321 npad->joycon_color = {
322 .attribute = ColorAttribute::NoController,
323 .left = {},
324 .right = {},
325 };
326 // HW seems to initialize the first 19 entries
327 for (std::size_t i = 0; i < 19; ++i) {
328 WriteEmptyEntry(npad);
329 }
330 }
331}
332
333void NPad::WriteEmptyEntry(NpadInternalState* npad) { 364void NPad::WriteEmptyEntry(NpadInternalState* npad) {
334 NPadGenericState dummy_pad_state{}; 365 NPadGenericState dummy_pad_state{};
335 NpadGcTriggerState dummy_gc_state{}; 366 NpadGcTriggerState dummy_gc_state{};
@@ -351,31 +382,20 @@ void NPad::WriteEmptyEntry(NpadInternalState* npad) {
351 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state); 382 npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
352} 383}
353 384
354void NPad::OnRelease() { 385void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) {
355 is_controller_initialized = false; 386 std::scoped_lock lock{*applet_resource_holder.shared_mutex};
356 for (std::size_t i = 0; i < controller_data.size(); ++i) { 387 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
357 auto& controller = controller_data[i];
358 service_context.CloseEvent(controller.styleset_changed_event);
359 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
360 VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_idx, {});
361 }
362 }
363}
364
365void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
366 std::scoped_lock lock{mutex};
367 auto& controller = GetControllerFromNpadIdType(npad_id);
368 const auto controller_type = controller.device->GetNpadStyleIndex(); 388 const auto controller_type = controller.device->GetNpadStyleIndex();
369 389
370 if (!controller.device->IsConnected() && controller.is_connected) { 390 if (!controller.device->IsConnected() && controller.is_connected) {
371 DisconnectNpad(npad_id); 391 DisconnectNpad(aruid, npad_id);
372 return; 392 return;
373 } 393 }
374 if (!controller.device->IsConnected()) { 394 if (!controller.device->IsConnected()) {
375 return; 395 return;
376 } 396 }
377 if (controller.device->IsConnected() && !controller.is_connected) { 397 if (controller.device->IsConnected() && !controller.is_connected) {
378 InitNewlyAddedController(npad_id); 398 InitNewlyAddedController(aruid, npad_id);
379 } 399 }
380 400
381 // This function is unique to yuzu for the turbo buttons and motion to work properly 401 // This function is unique to yuzu for the turbo buttons and motion to work properly
@@ -432,222 +452,232 @@ void NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
432} 452}
433 453
434void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 454void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
435 if (!IsControllerActivated()) { 455 if (ref_counter == 0) {
436 return; 456 return;
437 } 457 }
438 458
439 for (std::size_t i = 0; i < controller_data.size(); ++i) { 459 std::scoped_lock lock{*applet_resource_holder.shared_mutex};
440 auto& controller = controller_data[i]; 460 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
441 auto* npad = controller.shared_memory; 461 const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);
442 462 const auto aruid = data->aruid;
443 const auto& controller_type = controller.device->GetNpadStyleIndex();
444 463
445 if (controller_type == Core::HID::NpadStyleIndex::None || 464 if (!data->flag.is_assigned) {
446 !controller.device->IsConnected()) {
447 continue; 465 continue;
448 } 466 }
449 467
450 RequestPadStateUpdate(controller.device->GetNpadIdType()); 468 for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
451 auto& pad_state = controller.npad_pad_state; 469 auto& controller = controller_data[aruid_index][i];
452 auto& libnx_state = controller.npad_libnx_state; 470 controller.shared_memory =
453 auto& trigger_state = controller.npad_trigger_state; 471 &data->shared_memory_format->npad.npad_entry[i].internal_state;
454 472 auto* npad = controller.shared_memory;
455 // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate 473
456 // any controllers. 474 const auto& controller_type = controller.device->GetNpadStyleIndex();
457 libnx_state.connection_status.raw = 0; 475
458 libnx_state.connection_status.is_connected.Assign(1); 476 if (controller_type == Core::HID::NpadStyleIndex::None ||
459 switch (controller_type) { 477 !controller.device->IsConnected()) {
460 case Core::HID::NpadStyleIndex::None: 478 continue;
461 ASSERT(false); 479 }
462 break; 480
463 case Core::HID::NpadStyleIndex::ProController: 481 RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());
464 case Core::HID::NpadStyleIndex::NES: 482 auto& pad_state = controller.npad_pad_state;
465 case Core::HID::NpadStyleIndex::SNES: 483 auto& libnx_state = controller.npad_libnx_state;
466 case Core::HID::NpadStyleIndex::N64: 484 auto& trigger_state = controller.npad_trigger_state;
467 case Core::HID::NpadStyleIndex::SegaGenesis: 485
468 pad_state.connection_status.raw = 0; 486 // LibNX exclusively uses this section, so we always update it since LibNX doesn't
469 pad_state.connection_status.is_connected.Assign(1); 487 // activate any controllers.
470 pad_state.connection_status.is_wired.Assign(1); 488 libnx_state.connection_status.raw = 0;
471 489 libnx_state.connection_status.is_connected.Assign(1);
472 libnx_state.connection_status.is_wired.Assign(1); 490 switch (controller_type) {
473 pad_state.sampling_number = 491 case Core::HID::NpadStyleIndex::None:
474 npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1; 492 ASSERT(false);
475 npad->fullkey_lifo.WriteNextEntry(pad_state); 493 break;
476 break; 494 case Core::HID::NpadStyleIndex::ProController:
477 case Core::HID::NpadStyleIndex::Handheld: 495 case Core::HID::NpadStyleIndex::NES:
478 pad_state.connection_status.raw = 0; 496 case Core::HID::NpadStyleIndex::SNES:
479 pad_state.connection_status.is_connected.Assign(1); 497 case Core::HID::NpadStyleIndex::N64:
480 pad_state.connection_status.is_wired.Assign(1); 498 case Core::HID::NpadStyleIndex::SegaGenesis:
481 pad_state.connection_status.is_left_connected.Assign(1); 499 pad_state.connection_status.raw = 0;
482 pad_state.connection_status.is_right_connected.Assign(1); 500 pad_state.connection_status.is_connected.Assign(1);
483 pad_state.connection_status.is_left_wired.Assign(1); 501 pad_state.connection_status.is_wired.Assign(1);
484 pad_state.connection_status.is_right_wired.Assign(1); 502
485 503 libnx_state.connection_status.is_wired.Assign(1);
486 libnx_state.connection_status.is_wired.Assign(1); 504 pad_state.sampling_number =
487 libnx_state.connection_status.is_left_connected.Assign(1); 505 npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
488 libnx_state.connection_status.is_right_connected.Assign(1); 506 npad->fullkey_lifo.WriteNextEntry(pad_state);
489 libnx_state.connection_status.is_left_wired.Assign(1); 507 break;
490 libnx_state.connection_status.is_right_wired.Assign(1); 508 case Core::HID::NpadStyleIndex::Handheld:
491 pad_state.sampling_number = 509 pad_state.connection_status.raw = 0;
492 npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1; 510 pad_state.connection_status.is_connected.Assign(1);
493 npad->handheld_lifo.WriteNextEntry(pad_state); 511 pad_state.connection_status.is_wired.Assign(1);
494 break;
495 case Core::HID::NpadStyleIndex::JoyconDual:
496 pad_state.connection_status.raw = 0;
497 pad_state.connection_status.is_connected.Assign(1);
498 if (controller.is_dual_left_connected) {
499 pad_state.connection_status.is_left_connected.Assign(1); 512 pad_state.connection_status.is_left_connected.Assign(1);
513 pad_state.connection_status.is_right_connected.Assign(1);
514 pad_state.connection_status.is_left_wired.Assign(1);
515 pad_state.connection_status.is_right_wired.Assign(1);
516
517 libnx_state.connection_status.is_wired.Assign(1);
500 libnx_state.connection_status.is_left_connected.Assign(1); 518 libnx_state.connection_status.is_left_connected.Assign(1);
501 } 519 libnx_state.connection_status.is_right_connected.Assign(1);
502 if (controller.is_dual_right_connected) { 520 libnx_state.connection_status.is_left_wired.Assign(1);
521 libnx_state.connection_status.is_right_wired.Assign(1);
522 pad_state.sampling_number =
523 npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
524 npad->handheld_lifo.WriteNextEntry(pad_state);
525 break;
526 case Core::HID::NpadStyleIndex::JoyconDual:
527 pad_state.connection_status.raw = 0;
528 pad_state.connection_status.is_connected.Assign(1);
529 if (controller.is_dual_left_connected) {
530 pad_state.connection_status.is_left_connected.Assign(1);
531 libnx_state.connection_status.is_left_connected.Assign(1);
532 }
533 if (controller.is_dual_right_connected) {
534 pad_state.connection_status.is_right_connected.Assign(1);
535 libnx_state.connection_status.is_right_connected.Assign(1);
536 }
537
538 pad_state.sampling_number =
539 npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
540 npad->joy_dual_lifo.WriteNextEntry(pad_state);
541 break;
542 case Core::HID::NpadStyleIndex::JoyconLeft:
543 pad_state.connection_status.raw = 0;
544 pad_state.connection_status.is_connected.Assign(1);
545 pad_state.connection_status.is_left_connected.Assign(1);
546
547 libnx_state.connection_status.is_left_connected.Assign(1);
548 pad_state.sampling_number =
549 npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
550 npad->joy_left_lifo.WriteNextEntry(pad_state);
551 break;
552 case Core::HID::NpadStyleIndex::JoyconRight:
553 pad_state.connection_status.raw = 0;
554 pad_state.connection_status.is_connected.Assign(1);
503 pad_state.connection_status.is_right_connected.Assign(1); 555 pad_state.connection_status.is_right_connected.Assign(1);
556
504 libnx_state.connection_status.is_right_connected.Assign(1); 557 libnx_state.connection_status.is_right_connected.Assign(1);
558 pad_state.sampling_number =
559 npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
560 npad->joy_right_lifo.WriteNextEntry(pad_state);
561 break;
562 case Core::HID::NpadStyleIndex::GameCube:
563 pad_state.connection_status.raw = 0;
564 pad_state.connection_status.is_connected.Assign(1);
565 pad_state.connection_status.is_wired.Assign(1);
566
567 libnx_state.connection_status.is_wired.Assign(1);
568 pad_state.sampling_number =
569 npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
570 trigger_state.sampling_number =
571 npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
572 npad->fullkey_lifo.WriteNextEntry(pad_state);
573 npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
574 break;
575 case Core::HID::NpadStyleIndex::Pokeball:
576 pad_state.connection_status.raw = 0;
577 pad_state.connection_status.is_connected.Assign(1);
578 pad_state.sampling_number =
579 npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
580 npad->palma_lifo.WriteNextEntry(pad_state);
581 break;
582 default:
583 break;
505 } 584 }
506 585
507 pad_state.sampling_number = 586 libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
508 npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1; 587 libnx_state.l_stick = pad_state.l_stick;
509 npad->joy_dual_lifo.WriteNextEntry(pad_state); 588 libnx_state.r_stick = pad_state.r_stick;
510 break; 589 npad->system_ext_lifo.WriteNextEntry(pad_state);
511 case Core::HID::NpadStyleIndex::JoyconLeft:
512 pad_state.connection_status.raw = 0;
513 pad_state.connection_status.is_connected.Assign(1);
514 pad_state.connection_status.is_left_connected.Assign(1);
515
516 libnx_state.connection_status.is_left_connected.Assign(1);
517 pad_state.sampling_number =
518 npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
519 npad->joy_left_lifo.WriteNextEntry(pad_state);
520 break;
521 case Core::HID::NpadStyleIndex::JoyconRight:
522 pad_state.connection_status.raw = 0;
523 pad_state.connection_status.is_connected.Assign(1);
524 pad_state.connection_status.is_right_connected.Assign(1);
525
526 libnx_state.connection_status.is_right_connected.Assign(1);
527 pad_state.sampling_number =
528 npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
529 npad->joy_right_lifo.WriteNextEntry(pad_state);
530 break;
531 case Core::HID::NpadStyleIndex::GameCube:
532 pad_state.connection_status.raw = 0;
533 pad_state.connection_status.is_connected.Assign(1);
534 pad_state.connection_status.is_wired.Assign(1);
535
536 libnx_state.connection_status.is_wired.Assign(1);
537 pad_state.sampling_number =
538 npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
539 trigger_state.sampling_number =
540 npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
541 npad->fullkey_lifo.WriteNextEntry(pad_state);
542 npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
543 break;
544 case Core::HID::NpadStyleIndex::Pokeball:
545 pad_state.connection_status.raw = 0;
546 pad_state.connection_status.is_connected.Assign(1);
547 pad_state.sampling_number =
548 npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
549 npad->palma_lifo.WriteNextEntry(pad_state);
550 break;
551 default:
552 break;
553 }
554 590
555 libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; 591 press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
556 libnx_state.l_stick = pad_state.l_stick; 592 }
557 libnx_state.r_stick = pad_state.r_stick;
558 npad->system_ext_lifo.WriteNextEntry(pad_state);
559
560 press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
561 } 593 }
562} 594}
563 595
564void NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 596Result NPad::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set) {
565 hid_core.SetSupportedStyleTag(style_set); 597 std::scoped_lock lock{mutex};
566 598 hid_core.SetSupportedStyleTag({supported_style_set});
567 if (is_controller_initialized) { 599 const Result result = npad_resource.SetSupportedNpadStyleSet(aruid, supported_style_set);
568 return; 600 if (result.IsSuccess()) {
601 OnUpdate({});
569 } 602 }
570 603 return result;
571 // Once SetSupportedStyleSet is called controllers are fully initialized
572 is_controller_initialized = true;
573} 604}
574 605
575Core::HID::NpadStyleTag NPad::GetSupportedStyleSet() const { 606Result NPad::GetSupportedNpadStyleSet(u64 aruid,
576 if (!is_controller_initialized) { 607 Core::HID::NpadStyleSet& out_supported_style_set) const {
577 return {Core::HID::NpadStyleSet::None}; 608 std::scoped_lock lock{mutex};
609 const Result result = npad_resource.GetSupportedNpadStyleSet(out_supported_style_set, aruid);
610
611 if (result == ResultUndefinedStyleset) {
612 out_supported_style_set = Core::HID::NpadStyleSet::None;
613 return ResultSuccess;
578 } 614 }
579 return hid_core.GetSupportedStyleTag(); 615
616 return result;
580} 617}
581 618
582Result NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 619Result NPad::GetMaskedSupportedNpadStyleSet(
583 constexpr std::size_t max_number_npad_ids = 0xa; 620 u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const {
584 const auto length = data.size(); 621 std::scoped_lock lock{mutex};
585 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 622 const Result result =
586 const std::size_t elements = length / sizeof(u32); 623 npad_resource.GetMaskedSupportedNpadStyleSet(out_supported_style_set, aruid);
587 624
588 if (elements > max_number_npad_ids) { 625 if (result == ResultUndefinedStyleset) {
589 return InvalidArraySize; 626 out_supported_style_set = Core::HID::NpadStyleSet::None;
627 return ResultSuccess;
590 } 628 }
591 629
592 supported_npad_id_types.clear(); 630 return result;
593 supported_npad_id_types.resize(elements);
594 std::memcpy(supported_npad_id_types.data(), data.data(), length);
595 return ResultSuccess;
596} 631}
597 632
598void NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 633Result NPad::SetSupportedNpadIdType(u64 aruid,
599 const auto copy_amount = supported_npad_id_types.size() * sizeof(u32); 634 std::span<const Core::HID::NpadIdType> supported_npad_list) {
600 ASSERT(max_length <= copy_amount); 635 std::scoped_lock lock{mutex};
601 std::memcpy(data, supported_npad_id_types.data(), copy_amount); 636 if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
602} 637 return ResultInvalidArraySize;
638 }
603 639
604std::size_t NPad::GetSupportedNpadIdTypesSize() const { 640 Result result = npad_resource.SetSupportedNpadIdType(aruid, supported_npad_list);
605 return supported_npad_id_types.size();
606}
607 641
608void NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 642 if (result.IsSuccess()) {
609 if (joy_hold_type != NpadJoyHoldType::Horizontal && 643 OnUpdate({});
610 joy_hold_type != NpadJoyHoldType::Vertical) {
611 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
612 joy_hold_type);
613 return;
614 } 644 }
615 hold_type = joy_hold_type;
616}
617 645
618NpadJoyHoldType NPad::GetHoldType() const { 646 return result;
619 return hold_type;
620} 647}
621 648
622void NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { 649Result NPad::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
623 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) { 650 std::scoped_lock lock{mutex};
624 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual"); 651 return npad_resource.SetNpadJoyHoldType(aruid, hold_type);
625 return;
626 }
627
628 handheld_activation_mode = activation_mode;
629} 652}
630 653
631NpadHandheldActivationMode NPad::GetNpadHandheldActivationMode() const { 654Result NPad::GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const {
632 return handheld_activation_mode; 655 std::scoped_lock lock{mutex};
656 return npad_resource.GetNpadJoyHoldType(out_hold_type, aruid);
633} 657}
634 658
635void NPad::SetNpadCommunicationMode(NpadCommunicationMode communication_mode_) { 659Result NPad::SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode) {
636 communication_mode = communication_mode_; 660 std::scoped_lock lock{mutex};
661 Result result = npad_resource.SetNpadHandheldActivationMode(aruid, mode);
662 if (result.IsSuccess()) {
663 OnUpdate({});
664 }
665 return result;
637} 666}
638 667
639NpadCommunicationMode NPad::GetNpadCommunicationMode() const { 668Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const {
640 return communication_mode; 669 std::scoped_lock lock{mutex};
670 return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid);
641} 671}
642 672
643bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id, 673bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
644 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) { 674 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
645 if (!IsNpadIdValid(npad_id)) { 675 if (!IsNpadIdValid(npad_id)) {
646 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 676 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
647 return false; 677 return false;
648 } 678 }
649 679
650 auto& controller = GetControllerFromNpadIdType(npad_id); 680 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
651 if (controller.shared_memory->assignment_mode != assignment_mode) { 681 if (controller.shared_memory->assignment_mode != assignment_mode) {
652 controller.shared_memory->assignment_mode = assignment_mode; 682 controller.shared_memory->assignment_mode = assignment_mode;
653 } 683 }
@@ -658,17 +688,17 @@ bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType
658 688
659 if (assignment_mode == NpadJoyAssignmentMode::Dual) { 689 if (assignment_mode == NpadJoyAssignmentMode::Dual) {
660 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) { 690 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
661 DisconnectNpad(npad_id); 691 DisconnectNpad(aruid, npad_id);
662 controller.is_dual_left_connected = true; 692 controller.is_dual_left_connected = true;
663 controller.is_dual_right_connected = false; 693 controller.is_dual_right_connected = false;
664 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 694 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
665 return false; 695 return false;
666 } 696 }
667 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { 697 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
668 DisconnectNpad(npad_id); 698 DisconnectNpad(aruid, npad_id);
669 controller.is_dual_left_connected = false; 699 controller.is_dual_left_connected = false;
670 controller.is_dual_right_connected = true; 700 controller.is_dual_right_connected = true;
671 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 701 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
672 return false; 702 return false;
673 } 703 }
674 return false; 704 return false;
@@ -682,37 +712,38 @@ bool NPad::SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType
682 } 712 }
683 713
684 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { 714 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
685 DisconnectNpad(npad_id); 715 DisconnectNpad(aruid, npad_id);
686 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); 716 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
687 return false; 717 return false;
688 } 718 }
689 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { 719 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
690 DisconnectNpad(npad_id); 720 DisconnectNpad(aruid, npad_id);
691 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); 721 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
692 return false; 722 return false;
693 } 723 }
694 724
695 // We have two controllers connected to the same npad_id we need to split them 725 // We have two controllers connected to the same npad_id we need to split them
696 new_npad_id = hid_core.GetFirstDisconnectedNpadId(); 726 new_npad_id = hid_core.GetFirstDisconnectedNpadId();
697 auto& controller_2 = GetControllerFromNpadIdType(new_npad_id); 727 auto& controller_2 = GetControllerFromNpadIdType(aruid, new_npad_id);
698 DisconnectNpad(npad_id); 728 DisconnectNpad(aruid, npad_id);
699 if (npad_device_type == NpadJoyDeviceType::Left) { 729 if (npad_device_type == NpadJoyDeviceType::Left) {
700 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); 730 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
701 controller_2.is_dual_left_connected = false; 731 controller_2.is_dual_left_connected = false;
702 controller_2.is_dual_right_connected = true; 732 controller_2.is_dual_right_connected = true;
703 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); 733 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
704 } else { 734 } else {
705 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); 735 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
706 controller_2.is_dual_left_connected = true; 736 controller_2.is_dual_left_connected = true;
707 controller_2.is_dual_right_connected = false; 737 controller_2.is_dual_right_connected = false;
708 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true); 738 UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
709 } 739 }
710 return true; 740 return true;
711} 741}
712 742
713bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, 743bool NPad::VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
744 std::size_t device_index,
714 const Core::HID::VibrationValue& vibration_value) { 745 const Core::HID::VibrationValue& vibration_value) {
715 auto& controller = GetControllerFromNpadIdType(npad_id); 746 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
716 if (!controller.device->IsConnected()) { 747 if (!controller.device->IsConnected()) {
717 return false; 748 return false;
718 } 749 }
@@ -755,7 +786,8 @@ bool NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t d
755 return controller.device->SetVibration(device_index, vibration); 786 return controller.device->SetVibration(device_index, vibration);
756} 787}
757 788
758void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, 789void NPad::VibrateController(u64 aruid,
790 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
759 const Core::HID::VibrationValue& vibration_value) { 791 const Core::HID::VibrationValue& vibration_value) {
760 if (IsVibrationHandleValid(vibration_device_handle).IsError()) { 792 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
761 return; 793 return;
@@ -765,7 +797,7 @@ void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_d
765 return; 797 return;
766 } 798 }
767 799
768 auto& controller = GetControllerFromHandle(vibration_device_handle); 800 auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
769 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 801 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
770 802
771 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) { 803 if (!controller.vibration[device_index].device_mounted || !controller.device->IsConnected()) {
@@ -795,14 +827,14 @@ void NPad::VibrateController(const Core::HID::VibrationDeviceHandle& vibration_d
795 return; 827 return;
796 } 828 }
797 829
798 if (VibrateControllerAtIndex(controller.device->GetNpadIdType(), device_index, 830 if (VibrateControllerAtIndex(aruid, controller.device->GetNpadIdType(), device_index,
799 vibration_value)) { 831 vibration_value)) {
800 controller.vibration[device_index].latest_vibration_value = vibration_value; 832 controller.vibration[device_index].latest_vibration_value = vibration_value;
801 } 833 }
802} 834}
803 835
804void NPad::VibrateControllers( 836void NPad::VibrateControllers(
805 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 837 u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
806 std::span<const Core::HID::VibrationValue> vibration_values) { 838 std::span<const Core::HID::VibrationValue> vibration_values) {
807 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) { 839 if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
808 return; 840 return;
@@ -814,17 +846,17 @@ void NPad::VibrateControllers(
814 "this is undefined behavior!"); 846 "this is undefined behavior!");
815 847
816 for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) { 848 for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
817 VibrateController(vibration_device_handles[i], vibration_values[i]); 849 VibrateController(aruid, vibration_device_handles[i], vibration_values[i]);
818 } 850 }
819} 851}
820 852
821Core::HID::VibrationValue NPad::GetLastVibration( 853Core::HID::VibrationValue NPad::GetLastVibration(
822 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 854 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
823 if (IsVibrationHandleValid(vibration_device_handle).IsError()) { 855 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
824 return {}; 856 return {};
825 } 857 }
826 858
827 const auto& controller = GetControllerFromHandle(vibration_device_handle); 859 const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
828 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 860 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
829 return controller.vibration[device_index].latest_vibration_value; 861 return controller.vibration[device_index].latest_vibration_value;
830} 862}
@@ -835,14 +867,15 @@ void NPad::InitializeVibrationDevice(
835 return; 867 return;
836 } 868 }
837 869
870 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
838 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id); 871 const auto npad_index = static_cast<Core::HID::NpadIdType>(vibration_device_handle.npad_id);
839 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 872 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
840 InitializeVibrationDeviceAtIndex(npad_index, device_index); 873 InitializeVibrationDeviceAtIndex(aruid, npad_index, device_index);
841} 874}
842 875
843void NPad::InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, 876void NPad::InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
844 std::size_t device_index) { 877 std::size_t device_index) {
845 auto& controller = GetControllerFromNpadIdType(npad_id); 878 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
846 if (!Settings::values.vibration_enabled.GetValue()) { 879 if (!Settings::values.vibration_enabled.GetValue()) {
847 controller.vibration[device_index].device_mounted = false; 880 controller.vibration[device_index].device_mounted = false;
848 return; 881 return;
@@ -857,60 +890,50 @@ void NPad::SetPermitVibrationSession(bool permit_vibration_session) {
857} 890}
858 891
859bool NPad::IsVibrationDeviceMounted( 892bool NPad::IsVibrationDeviceMounted(
860 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const { 893 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
861 if (IsVibrationHandleValid(vibration_device_handle).IsError()) { 894 if (IsVibrationHandleValid(vibration_device_handle).IsError()) {
862 return false; 895 return false;
863 } 896 }
864 897
865 const auto& controller = GetControllerFromHandle(vibration_device_handle); 898 const auto& controller = GetControllerFromHandle(aruid, vibration_device_handle);
866 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index); 899 const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
867 return controller.vibration[device_index].device_mounted; 900 return controller.vibration[device_index].device_mounted;
868} 901}
869 902
870Kernel::KReadableEvent& NPad::GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id) { 903Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
871 if (!IsNpadIdValid(npad_id)) { 904 Core::HID::NpadIdType npad_id) {
872 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 905 std::scoped_lock lock{mutex};
873 // Fallback to player 1 906 return npad_resource.AcquireNpadStyleSetUpdateEventHandle(aruid, out_event, npad_id);
874 const auto& controller = GetControllerFromNpadIdType(Core::HID::NpadIdType::Player1);
875 return controller.styleset_changed_event->GetReadableEvent();
876 }
877
878 const auto& controller = GetControllerFromNpadIdType(npad_id);
879 return controller.styleset_changed_event->GetReadableEvent();
880}
881
882void NPad::SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const {
883 const auto& controller = GetControllerFromNpadIdType(npad_id);
884 controller.styleset_changed_event->Signal();
885} 907}
886 908
887void NPad::AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id) { 909void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
888 UpdateControllerAt(controller, npad_id, true); 910 Core::HID::NpadIdType npad_id) {
911 UpdateControllerAt(aruid, controller, npad_id, true);
889} 912}
890 913
891void NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type, Core::HID::NpadIdType npad_id, 914void NPad::UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex type,
892 bool connected) { 915 Core::HID::NpadIdType npad_id, bool connected) {
893 auto& controller = GetControllerFromNpadIdType(npad_id); 916 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
894 if (!connected) { 917 if (!connected) {
895 DisconnectNpad(npad_id); 918 DisconnectNpad(aruid, npad_id);
896 return; 919 return;
897 } 920 }
898 921
899 controller.device->SetNpadStyleIndex(type); 922 controller.device->SetNpadStyleIndex(type);
900 InitNewlyAddedController(npad_id); 923 InitNewlyAddedController(aruid, npad_id);
901} 924}
902 925
903Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 926Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
904 if (!IsNpadIdValid(npad_id)) { 927 if (!IsNpadIdValid(npad_id)) {
905 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 928 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
906 return InvalidNpadId; 929 return ResultInvalidNpadId;
907 } 930 }
908 931
909 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); 932 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
910 auto& controller = GetControllerFromNpadIdType(npad_id); 933 auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
911 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) { 934 for (std::size_t device_idx = 0; device_idx < controller.vibration.size(); ++device_idx) {
912 // Send an empty vibration to stop any vibrations. 935 // Send an empty vibration to stop any vibrations.
913 VibrateControllerAtIndex(npad_id, device_idx, {}); 936 VibrateControllerAtIndex(aruid, npad_id, device_idx, {});
914 controller.vibration[device_idx].device_mounted = false; 937 controller.vibration[device_idx].device_mounted = false;
915 } 938 }
916 939
@@ -944,71 +967,48 @@ Result NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
944 controller.is_dual_right_connected = true; 967 controller.is_dual_right_connected = true;
945 controller.is_connected = false; 968 controller.is_connected = false;
946 controller.device->Disconnect(); 969 controller.device->Disconnect();
947 SignalStyleSetChangedEvent(npad_id); 970 npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
948 WriteEmptyEntry(shared_memory); 971 WriteEmptyEntry(shared_memory);
949 return ResultSuccess; 972 return ResultSuccess;
950} 973}
951 974
952Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor( 975Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
953 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const { 976 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
977 bool& is_firmware_available) const {
954 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); 978 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
955 if (is_valid.IsError()) { 979 if (is_valid.IsError()) {
956 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 980 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
957 return is_valid; 981 return is_valid;
958 } 982 }
959 983
960 const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); 984 const auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
961 is_firmware_available = sixaxis_properties.is_firmware_update_available != 0; 985 is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
962 return ResultSuccess; 986 return ResultSuccess;
963} 987}
964 988
965Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned( 989Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
966 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 990 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
967 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle); 991 const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
968 if (is_valid.IsError()) { 992 if (is_valid.IsError()) {
969 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw); 993 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
970 return is_valid; 994 return is_valid;
971 } 995 }
972 996
973 auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle); 997 auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
974 sixaxis_properties.is_newly_assigned.Assign(0); 998 sixaxis_properties.is_newly_assigned.Assign(0);
975 999
976 return ResultSuccess; 1000 return ResultSuccess;
977} 1001}
978 1002
979NpadSixAxisSensorLifo& NPad::GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id) { 1003Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
980 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_fullkey_lifo;
981}
982
983NpadSixAxisSensorLifo& NPad::GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id) {
984 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_handheld_lifo;
985}
986
987NpadSixAxisSensorLifo& NPad::GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id) {
988 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_left_lifo;
989}
990
991NpadSixAxisSensorLifo& NPad::GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id) {
992 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_dual_right_lifo;
993}
994
995NpadSixAxisSensorLifo& NPad::GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id) {
996 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_left_lifo;
997}
998
999NpadSixAxisSensorLifo& NPad::GetSixAxisRightLifo(Core::HID::NpadIdType npad_id) {
1000 return GetControllerFromNpadIdType(npad_id).shared_memory->sixaxis_right_lifo;
1001}
1002
1003Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1004 Core::HID::NpadIdType npad_id_2) { 1004 Core::HID::NpadIdType npad_id_2) {
1005 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1005 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1006 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1006 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1007 npad_id_2); 1007 npad_id_2);
1008 return InvalidNpadId; 1008 return ResultInvalidNpadId;
1009 } 1009 }
1010 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); 1010 auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1);
1011 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); 1011 auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2);
1012 auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); 1012 auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
1013 auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); 1013 auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
1014 1014
@@ -1055,51 +1055,62 @@ Result NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1055 } 1055 }
1056 1056
1057 // Disconnect the joycons and connect them as dual joycon at the first index. 1057 // Disconnect the joycons and connect them as dual joycon at the first index.
1058 DisconnectNpad(npad_id_1); 1058 DisconnectNpad(aruid, npad_id_1);
1059 DisconnectNpad(npad_id_2); 1059 DisconnectNpad(aruid, npad_id_2);
1060 controller_1.is_dual_left_connected = true; 1060 controller_1.is_dual_left_connected = true;
1061 controller_1.is_dual_right_connected = true; 1061 controller_1.is_dual_right_connected = true;
1062 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1); 1062 AddNewControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1063 return ResultSuccess; 1063 return ResultSuccess;
1064} 1064}
1065 1065
1066void NPad::StartLRAssignmentMode() { 1066Result NPad::StartLrAssignmentMode(u64 aruid) {
1067 // Nothing internally is used for lr assignment mode. Since we have the ability to set the 1067 std::scoped_lock lock{mutex};
1068 // controller types from boot, it doesn't really matter about showing a selection screen 1068 bool is_enabled{};
1069 is_in_lr_assignment_mode = true; 1069 Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
1070 if (result.IsSuccess() && is_enabled == false) {
1071 result = npad_resource.SetLrAssignmentMode(aruid, true);
1072 }
1073 return result;
1070} 1074}
1071 1075
1072void NPad::StopLRAssignmentMode() { 1076Result NPad::StopLrAssignmentMode(u64 aruid) {
1073 is_in_lr_assignment_mode = false; 1077 std::scoped_lock lock{mutex};
1078 bool is_enabled{};
1079 Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
1080 if (result.IsSuccess() && is_enabled == true) {
1081 result = npad_resource.SetLrAssignmentMode(aruid, false);
1082 }
1083 return result;
1074} 1084}
1075 1085
1076Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2) { 1086Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
1087 Core::HID::NpadIdType npad_id_2) {
1077 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1088 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1078 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1089 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1079 npad_id_2); 1090 npad_id_2);
1080 return InvalidNpadId; 1091 return ResultInvalidNpadId;
1081 } 1092 }
1082 if (npad_id_1 == Core::HID::NpadIdType::Handheld || 1093 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1083 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || 1094 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1084 npad_id_2 == Core::HID::NpadIdType::Other) { 1095 npad_id_2 == Core::HID::NpadIdType::Other) {
1085 return ResultSuccess; 1096 return ResultSuccess;
1086 } 1097 }
1087 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; 1098 const auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1).device;
1088 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; 1099 const auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2).device;
1089 const auto type_index_1 = controller_1->GetNpadStyleIndex(); 1100 const auto type_index_1 = controller_1->GetNpadStyleIndex();
1090 const auto type_index_2 = controller_2->GetNpadStyleIndex(); 1101 const auto type_index_2 = controller_2->GetNpadStyleIndex();
1091 const auto is_connected_1 = controller_1->IsConnected(); 1102 const auto is_connected_1 = controller_1->IsConnected();
1092 const auto is_connected_2 = controller_2->IsConnected(); 1103 const auto is_connected_2 = controller_2->IsConnected();
1093 1104
1094 if (!IsControllerSupported(type_index_1) && is_connected_1) { 1105 if (!npad_resource.IsControllerSupported(aruid, type_index_1) && is_connected_1) {
1095 return NpadNotConnected; 1106 return ResultNpadNotConnected;
1096 } 1107 }
1097 if (!IsControllerSupported(type_index_2) && is_connected_2) { 1108 if (!npad_resource.IsControllerSupported(aruid, type_index_2) && is_connected_2) {
1098 return NpadNotConnected; 1109 return ResultNpadNotConnected;
1099 } 1110 }
1100 1111
1101 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); 1112 UpdateControllerAt(aruid, type_index_2, npad_id_1, is_connected_2);
1102 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); 1113 UpdateControllerAt(aruid, type_index_1, npad_id_2, is_connected_1);
1103 1114
1104 return ResultSuccess; 1115 return ResultSuccess;
1105} 1116}
@@ -1107,68 +1118,68 @@ Result NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::Npad
1107Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const { 1118Result NPad::GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const {
1108 if (!IsNpadIdValid(npad_id)) { 1119 if (!IsNpadIdValid(npad_id)) {
1109 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1120 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1110 return InvalidNpadId; 1121 return ResultInvalidNpadId;
1111 } 1122 }
1112 const auto& controller = GetControllerFromNpadIdType(npad_id).device; 1123 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
1124 const auto& controller = GetControllerFromNpadIdType(aruid, npad_id).device;
1113 pattern = controller->GetLedPattern(); 1125 pattern = controller->GetLedPattern();
1114 return ResultSuccess; 1126 return ResultSuccess;
1115} 1127}
1116 1128
1117Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 1129Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
1118 bool& is_valid) const { 1130 Core::HID::NpadIdType npad_id) const {
1119 if (!IsNpadIdValid(npad_id)) { 1131 std::scoped_lock lock{mutex};
1120 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1132 return npad_resource.GetHomeProtectionEnabled(out_is_enabled, aruid, npad_id);
1121 return InvalidNpadId;
1122 }
1123 const auto& controller = GetControllerFromNpadIdType(npad_id);
1124 is_valid = controller.unintended_home_button_input_protection;
1125 return ResultSuccess;
1126} 1133}
1127 1134
1128Result NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1135Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
1129 Core::HID::NpadIdType npad_id) { 1136 bool is_enabled) {
1130 if (!IsNpadIdValid(npad_id)) { 1137 std::scoped_lock lock{mutex};
1131 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1138 return npad_resource.SetHomeProtectionEnabled(aruid, npad_id, is_enabled);
1132 return InvalidNpadId;
1133 }
1134 auto& controller = GetControllerFromNpadIdType(npad_id);
1135 controller.unintended_home_button_input_protection = is_protection_enabled;
1136 return ResultSuccess;
1137} 1139}
1138 1140
1139void NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1141void NPad::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
1140 analog_stick_use_center_clamp = use_center_clamp; 1142 std::scoped_lock lock{mutex};
1143 npad_resource.SetNpadAnalogStickUseCenterClamp(aruid, is_enabled);
1141} 1144}
1142 1145
1143void NPad::ClearAllConnectedControllers() { 1146void NPad::ClearAllConnectedControllers() {
1144 for (auto& controller : controller_data) { 1147 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
1145 if (controller.device->IsConnected() && 1148 for (auto& controller : controller_data[aruid_index]) {
1146 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) { 1149 if (controller.device->IsConnected() &&
1147 controller.device->Disconnect(); 1150 controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
1148 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1151 controller.device->Disconnect();
1152 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1153 }
1149 } 1154 }
1150 } 1155 }
1151} 1156}
1152 1157
1153void NPad::DisconnectAllConnectedControllers() { 1158void NPad::DisconnectAllConnectedControllers() {
1154 for (auto& controller : controller_data) { 1159 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
1155 controller.device->Disconnect(); 1160 for (auto& controller : controller_data[aruid_index]) {
1161 controller.device->Disconnect();
1162 }
1156 } 1163 }
1157} 1164}
1158 1165
1159void NPad::ConnectAllDisconnectedControllers() { 1166void NPad::ConnectAllDisconnectedControllers() {
1160 for (auto& controller : controller_data) { 1167 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
1161 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None && 1168 for (auto& controller : controller_data[aruid_index]) {
1162 !controller.device->IsConnected()) { 1169 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
1163 controller.device->Connect(); 1170 !controller.device->IsConnected()) {
1171 controller.device->Connect();
1172 }
1164 } 1173 }
1165 } 1174 }
1166} 1175}
1167 1176
1168void NPad::ClearAllControllers() { 1177void NPad::ClearAllControllers() {
1169 for (auto& controller : controller_data) { 1178 for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
1170 controller.device->Disconnect(); 1179 for (auto& controller : controller_data[aruid_index]) {
1171 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None); 1180 controller.device->Disconnect();
1181 controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
1182 }
1172 } 1183 }
1173} 1184}
1174 1185
@@ -1176,128 +1187,105 @@ Core::HID::NpadButton NPad::GetAndResetPressState() {
1176 return static_cast<Core::HID::NpadButton>(press_state.exchange(0)); 1187 return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
1177} 1188}
1178 1189
1179void NPad::ApplyNpadSystemCommonPolicy() { 1190Result NPad::ApplyNpadSystemCommonPolicy(u64 aruid) {
1180 Core::HID::NpadStyleTag styletag{}; 1191 std::scoped_lock lock{mutex};
1181 styletag.fullkey.Assign(1); 1192 const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, false);
1182 styletag.handheld.Assign(1); 1193 if (result.IsSuccess()) {
1183 styletag.joycon_dual.Assign(1); 1194 OnUpdate({});
1184 styletag.system_ext.Assign(1); 1195 }
1185 styletag.system.Assign(1); 1196 return result;
1186 SetSupportedStyleSet(styletag); 1197}
1187
1188 SetNpadHandheldActivationMode(NpadHandheldActivationMode::Dual);
1189
1190 supported_npad_id_types.clear();
1191 supported_npad_id_types.resize(10);
1192 supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
1193 supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
1194 supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
1195 supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
1196 supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
1197 supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
1198 supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
1199 supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
1200 supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
1201 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
1202}
1203
1204bool NPad::IsControllerSupported(Core::HID::NpadStyleIndex controller) const {
1205 if (controller == Core::HID::NpadStyleIndex::Handheld) {
1206 const bool support_handheld =
1207 std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
1208 Core::HID::NpadIdType::Handheld) != supported_npad_id_types.end();
1209 // Handheld is not even a supported type, lets stop here
1210 if (!support_handheld) {
1211 return false;
1212 }
1213 // Handheld shouldn't be supported in docked mode
1214 if (Settings::IsDockedMode()) {
1215 return false;
1216 }
1217 1198
1218 return true; 1199Result NPad::ApplyNpadSystemCommonPolicyFull(u64 aruid) {
1219 } 1200 std::scoped_lock lock{mutex};
1220 1201 const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, true);
1221 if (std::any_of(supported_npad_id_types.begin(), supported_npad_id_types.end(), 1202 if (result.IsSuccess()) {
1222 [](Core::HID::NpadIdType npad_id) { 1203 OnUpdate({});
1223 return npad_id <= Core::HID::NpadIdType::Player8;
1224 })) {
1225 Core::HID::NpadStyleTag style = GetSupportedStyleSet();
1226 switch (controller) {
1227 case Core::HID::NpadStyleIndex::ProController:
1228 return style.fullkey.As<bool>();
1229 case Core::HID::NpadStyleIndex::JoyconDual:
1230 return style.joycon_dual.As<bool>();
1231 case Core::HID::NpadStyleIndex::JoyconLeft:
1232 return style.joycon_left.As<bool>();
1233 case Core::HID::NpadStyleIndex::JoyconRight:
1234 return style.joycon_right.As<bool>();
1235 case Core::HID::NpadStyleIndex::GameCube:
1236 return style.gamecube.As<bool>();
1237 case Core::HID::NpadStyleIndex::Pokeball:
1238 return style.palma.As<bool>();
1239 case Core::HID::NpadStyleIndex::NES:
1240 return style.lark.As<bool>();
1241 case Core::HID::NpadStyleIndex::SNES:
1242 return style.lucia.As<bool>();
1243 case Core::HID::NpadStyleIndex::N64:
1244 return style.lagoon.As<bool>();
1245 case Core::HID::NpadStyleIndex::SegaGenesis:
1246 return style.lager.As<bool>();
1247 default:
1248 return false;
1249 }
1250 } 1204 }
1205 return result;
1206}
1207
1208Result NPad::ClearNpadSystemCommonPolicy(u64 aruid) {
1209 std::scoped_lock lock{mutex};
1210 const Result result = npad_resource.ClearNpadSystemCommonPolicy(aruid);
1211 if (result.IsSuccess()) {
1212 OnUpdate({});
1213 }
1214 return result;
1215}
1216
1217void NPad::SetRevision(u64 aruid, NpadRevision revision) {
1218 npad_resource.SetNpadRevision(aruid, revision);
1219}
1220
1221NpadRevision NPad::GetRevision(u64 aruid) {
1222 return npad_resource.GetNpadRevision(aruid);
1223}
1224
1225Result NPad::RegisterAppletResourceUserId(u64 aruid) {
1226 return npad_resource.RegisterAppletResourceUserId(aruid);
1227}
1228
1229void NPad::UnregisterAppletResourceUserId(u64 aruid) {
1230 npad_resource.UnregisterAppletResourceUserId(aruid);
1231}
1251 1232
1252 return false; 1233void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
1234 std::recursive_mutex* shared_mutex) {
1235 applet_resource_holder.applet_resource = resource;
1236 applet_resource_holder.shared_mutex = shared_mutex;
1237 applet_resource_holder.shared_npad_resource = &npad_resource;
1253} 1238}
1254 1239
1255NPad::NpadControllerData& NPad::GetControllerFromHandle( 1240NPad::NpadControllerData& NPad::GetControllerFromHandle(
1256 const Core::HID::VibrationDeviceHandle& device_handle) { 1241 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) {
1257 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1242 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1258 return GetControllerFromNpadIdType(npad_id); 1243 return GetControllerFromNpadIdType(aruid, npad_id);
1259} 1244}
1260 1245
1261const NPad::NpadControllerData& NPad::GetControllerFromHandle( 1246const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1262 const Core::HID::VibrationDeviceHandle& device_handle) const { 1247 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const {
1263 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1248 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1264 return GetControllerFromNpadIdType(npad_id); 1249 return GetControllerFromNpadIdType(aruid, npad_id);
1265} 1250}
1266 1251
1267NPad::NpadControllerData& NPad::GetControllerFromHandle( 1252NPad::NpadControllerData& NPad::GetControllerFromHandle(
1268 const Core::HID::SixAxisSensorHandle& device_handle) { 1253 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) {
1269 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1254 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1270 return GetControllerFromNpadIdType(npad_id); 1255 return GetControllerFromNpadIdType(aruid, npad_id);
1271} 1256}
1272 1257
1273const NPad::NpadControllerData& NPad::GetControllerFromHandle( 1258const NPad::NpadControllerData& NPad::GetControllerFromHandle(
1274 const Core::HID::SixAxisSensorHandle& device_handle) const { 1259 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const {
1275 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id); 1260 const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
1276 return GetControllerFromNpadIdType(npad_id); 1261 return GetControllerFromNpadIdType(aruid, npad_id);
1277} 1262}
1278 1263
1279NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) { 1264NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(u64 aruid,
1265 Core::HID::NpadIdType npad_id) {
1280 if (!IsNpadIdValid(npad_id)) { 1266 if (!IsNpadIdValid(npad_id)) {
1281 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1267 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1282 npad_id = Core::HID::NpadIdType::Player1; 1268 npad_id = Core::HID::NpadIdType::Player1;
1283 } 1269 }
1284 const auto npad_index = NpadIdTypeToIndex(npad_id); 1270 const auto npad_index = NpadIdTypeToIndex(npad_id);
1285 return controller_data[npad_index]; 1271 const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
1272 return controller_data[aruid_index][npad_index];
1286} 1273}
1287 1274
1288const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType( 1275const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
1289 Core::HID::NpadIdType npad_id) const { 1276 u64 aruid, Core::HID::NpadIdType npad_id) const {
1290 if (!IsNpadIdValid(npad_id)) { 1277 if (!IsNpadIdValid(npad_id)) {
1291 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1278 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1292 npad_id = Core::HID::NpadIdType::Player1; 1279 npad_id = Core::HID::NpadIdType::Player1;
1293 } 1280 }
1294 const auto npad_index = NpadIdTypeToIndex(npad_id); 1281 const auto npad_index = NpadIdTypeToIndex(npad_id);
1295 return controller_data[npad_index]; 1282 const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
1283 return controller_data[aruid_index][npad_index];
1296} 1284}
1297 1285
1298Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( 1286Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1299 const Core::HID::SixAxisSensorHandle& sixaxis_handle) { 1287 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1300 auto& controller = GetControllerFromHandle(sixaxis_handle); 1288 auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
1301 switch (sixaxis_handle.npad_type) { 1289 switch (sixaxis_handle.npad_type) {
1302 case Core::HID::NpadStyleIndex::ProController: 1290 case Core::HID::NpadStyleIndex::ProController:
1303 case Core::HID::NpadStyleIndex::Pokeball: 1291 case Core::HID::NpadStyleIndex::Pokeball:
@@ -1319,8 +1307,8 @@ Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1319} 1307}
1320 1308
1321const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties( 1309const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1322 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const { 1310 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1323 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1311 const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
1324 switch (sixaxis_handle.npad_type) { 1312 switch (sixaxis_handle.npad_type) {
1325 case Core::HID::NpadStyleIndex::ProController: 1313 case Core::HID::NpadStyleIndex::ProController:
1326 case Core::HID::NpadStyleIndex::Pokeball: 1314 case Core::HID::NpadStyleIndex::Pokeball:
@@ -1342,7 +1330,8 @@ const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
1342} 1330}
1343 1331
1344AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) { 1332AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
1345 const auto& shared_memory = GetControllerFromNpadIdType(npad_id).shared_memory; 1333 const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
1334 const auto& shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory;
1346 1335
1347 return { 1336 return {
1348 .ui_variant = 0, 1337 .ui_variant = 0,
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 4e2412356..8ab333064 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -11,6 +11,7 @@
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "core/hid/hid_types.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 13#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/controllers/npad/npad_resource.h"
14#include "core/hle/service/hid/controllers/types/npad_types.h" 15#include "core/hle/service/hid/controllers/types/npad_types.h"
15 16
16namespace Core::HID { 17namespace Core::HID {
@@ -30,111 +31,121 @@ class ServiceContext;
30union Result; 31union Result;
31 32
32namespace Service::HID { 33namespace Service::HID {
34class AppletResource;
33struct NpadInternalState; 35struct NpadInternalState;
34struct NpadSixAxisSensorLifo; 36struct NpadSixAxisSensorLifo;
35struct NpadSharedMemoryFormat; 37struct NpadSharedMemoryFormat;
36 38
37class NPad final : public ControllerBase { 39class NPad final {
38public: 40public:
39 explicit NPad(Core::HID::HIDCore& hid_core_, NpadSharedMemoryFormat& npad_shared_memory_format, 41 explicit NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_);
40 KernelHelpers::ServiceContext& service_context_); 42 ~NPad();
41 ~NPad() override;
42 43
43 // Called when the controller is initialized 44 Result Activate();
44 void OnInit() override; 45 Result Activate(u64 aruid);
45 46
46 // When the controller is released 47 Result ActivateNpadResource();
47 void OnRelease() override; 48 Result ActivateNpadResource(u64 aruid);
48 49
49 // When the controller is requesting an update for the shared memory 50 // When the controller is requesting an update for the shared memory
50 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; 51 void OnUpdate(const Core::Timing::CoreTiming& core_timing);
51 52
52 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 53 Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set);
53 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 54 Result GetSupportedNpadStyleSet(u64 aruid,
55 Core::HID::NpadStyleSet& out_supported_style_set) const;
56 Result GetMaskedSupportedNpadStyleSet(u64 aruid,
57 Core::HID::NpadStyleSet& out_supported_style_set) const;
54 58
55 Result SetSupportedNpadIdTypes(std::span<const u8> data); 59 Result SetSupportedNpadIdType(u64 aruid,
56 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 60 std::span<const Core::HID::NpadIdType> supported_npad_list);
57 std::size_t GetSupportedNpadIdTypesSize() const;
58 61
59 void SetHoldType(NpadJoyHoldType joy_hold_type); 62 Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
60 NpadJoyHoldType GetHoldType() const; 63 Result GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const;
61 64
62 void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); 65 Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode);
63 NpadHandheldActivationMode GetNpadHandheldActivationMode() const; 66 Result GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const;
64 67
65 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 68 bool SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
66 NpadCommunicationMode GetNpadCommunicationMode() const;
67
68 bool SetNpadMode(Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
69 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode); 69 NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode);
70 70
71 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, 71 bool VibrateControllerAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
72 std::size_t device_index,
72 const Core::HID::VibrationValue& vibration_value); 73 const Core::HID::VibrationValue& vibration_value);
73 74
74 void VibrateController(const Core::HID::VibrationDeviceHandle& vibration_device_handle, 75 void VibrateController(u64 aruid,
76 const Core::HID::VibrationDeviceHandle& vibration_device_handle,
75 const Core::HID::VibrationValue& vibration_value); 77 const Core::HID::VibrationValue& vibration_value);
76 78
77 void VibrateControllers( 79 void VibrateControllers(
78 std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles, 80 u64 aruid, std::span<const Core::HID::VibrationDeviceHandle> vibration_device_handles,
79 std::span<const Core::HID::VibrationValue> vibration_values); 81 std::span<const Core::HID::VibrationValue> vibration_values);
80 82
81 Core::HID::VibrationValue GetLastVibration( 83 Core::HID::VibrationValue GetLastVibration(
82 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; 84 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
83 85
84 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle); 86 void InitializeVibrationDevice(const Core::HID::VibrationDeviceHandle& vibration_device_handle);
85 87
86 void InitializeVibrationDeviceAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index); 88 void InitializeVibrationDeviceAtIndex(u64 aruid, Core::HID::NpadIdType npad_id,
89 std::size_t device_index);
87 90
88 void SetPermitVibrationSession(bool permit_vibration_session); 91 void SetPermitVibrationSession(bool permit_vibration_session);
89 92
90 bool IsVibrationDeviceMounted( 93 bool IsVibrationDeviceMounted(
91 const Core::HID::VibrationDeviceHandle& vibration_device_handle) const; 94 u64 aruid, const Core::HID::VibrationDeviceHandle& vibration_device_handle) const;
92 95
93 Kernel::KReadableEvent& GetStyleSetChangedEvent(Core::HID::NpadIdType npad_id); 96 Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
94 void SignalStyleSetChangedEvent(Core::HID::NpadIdType npad_id) const; 97 Core::HID::NpadIdType npad_id);
95 98
96 // Adds a new controller at an index. 99 // Adds a new controller at an index.
97 void AddNewControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id); 100 void AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
101 Core::HID::NpadIdType npad_id);
98 // Adds a new controller at an index with connection status. 102 // Adds a new controller at an index with connection status.
99 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, 103 void UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
100 bool connected); 104 Core::HID::NpadIdType npad_id, bool connected);
101 105
102 Result DisconnectNpad(Core::HID::NpadIdType npad_id); 106 Result DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id);
103 107
104 Result IsFirmwareUpdateAvailableForSixAxisSensor( 108 Result IsFirmwareUpdateAvailableForSixAxisSensor(
105 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const; 109 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
110 bool& is_firmware_available) const;
106 Result ResetIsSixAxisSensorDeviceNewlyAssigned( 111 Result ResetIsSixAxisSensorDeviceNewlyAssigned(
107 const Core::HID::SixAxisSensorHandle& sixaxis_handle); 112 u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle);
108
109 NpadSixAxisSensorLifo& GetSixAxisFullkeyLifo(Core::HID::NpadIdType npad_id);
110 NpadSixAxisSensorLifo& GetSixAxisHandheldLifo(Core::HID::NpadIdType npad_id);
111 NpadSixAxisSensorLifo& GetSixAxisDualLeftLifo(Core::HID::NpadIdType npad_id);
112 NpadSixAxisSensorLifo& GetSixAxisDualRightLifo(Core::HID::NpadIdType npad_id);
113 NpadSixAxisSensorLifo& GetSixAxisLeftLifo(Core::HID::NpadIdType npad_id);
114 NpadSixAxisSensorLifo& GetSixAxisRightLifo(Core::HID::NpadIdType npad_id);
115 113
116 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const; 114 Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
117 Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id, 115
118 bool& is_enabled) const; 116 Result IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
119 Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 117 Core::HID::NpadIdType npad_id) const;
120 Core::HID::NpadIdType npad_id); 118 Result EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
121 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 119 bool is_enabled);
120
121 void SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);
122 void ClearAllConnectedControllers(); 122 void ClearAllConnectedControllers();
123 void DisconnectAllConnectedControllers(); 123 void DisconnectAllConnectedControllers();
124 void ConnectAllDisconnectedControllers(); 124 void ConnectAllDisconnectedControllers();
125 void ClearAllControllers(); 125 void ClearAllControllers();
126 126
127 Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 127 Result MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
128 Core::HID::NpadIdType npad_id_2); 128 Core::HID::NpadIdType npad_id_2);
129 void StartLRAssignmentMode(); 129 Result StartLrAssignmentMode(u64 aruid);
130 void StopLRAssignmentMode(); 130 Result StopLrAssignmentMode(u64 aruid);
131 Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); 131 Result SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
132 Core::HID::NpadIdType npad_id_2);
132 133
133 // Logical OR for all buttons presses on all controllers 134 // Logical OR for all buttons presses on all controllers
134 // Specifically for cheat engine and other features. 135 // Specifically for cheat engine and other features.
135 Core::HID::NpadButton GetAndResetPressState(); 136 Core::HID::NpadButton GetAndResetPressState();
136 137
137 void ApplyNpadSystemCommonPolicy(); 138 Result ApplyNpadSystemCommonPolicy(u64 aruid);
139 Result ApplyNpadSystemCommonPolicyFull(u64 aruid);
140 Result ClearNpadSystemCommonPolicy(u64 aruid);
141
142 void SetRevision(u64 aruid, NpadRevision revision);
143 NpadRevision GetRevision(u64 aruid);
144
145 Result RegisterAppletResourceUserId(u64 aruid);
146 void UnregisterAppletResourceUserId(u64 aruid);
147 void SetNpadExternals(std::shared_ptr<AppletResource> resource,
148 std::recursive_mutex* shared_mutex);
138 149
139 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); 150 AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id);
140 151
@@ -146,12 +157,10 @@ private:
146 }; 157 };
147 158
148 struct NpadControllerData { 159 struct NpadControllerData {
149 Kernel::KEvent* styleset_changed_event{};
150 NpadInternalState* shared_memory = nullptr; 160 NpadInternalState* shared_memory = nullptr;
151 Core::HID::EmulatedController* device = nullptr; 161 Core::HID::EmulatedController* device = nullptr;
152 162
153 std::array<VibrationData, 2> vibration{}; 163 std::array<VibrationData, 2> vibration{};
154 bool unintended_home_button_input_protection{};
155 bool is_connected{}; 164 bool is_connected{};
156 165
157 // Dual joycons can have only one side connected 166 // Dual joycons can have only one side connected
@@ -166,39 +175,40 @@ private:
166 }; 175 };
167 176
168 void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx); 177 void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
169 void InitNewlyAddedController(Core::HID::NpadIdType npad_id); 178 void InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id);
170 bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const; 179 void RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id);
171 void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
172 void WriteEmptyEntry(NpadInternalState* npad); 180 void WriteEmptyEntry(NpadInternalState* npad);
173 181
174 NpadControllerData& GetControllerFromHandle( 182 NpadControllerData& GetControllerFromHandle(
175 const Core::HID::VibrationDeviceHandle& device_handle); 183 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle);
176 const NpadControllerData& GetControllerFromHandle( 184 const NpadControllerData& GetControllerFromHandle(
177 const Core::HID::VibrationDeviceHandle& device_handle) const; 185 u64 aruid, const Core::HID::VibrationDeviceHandle& device_handle) const;
178 NpadControllerData& GetControllerFromHandle( 186 NpadControllerData& GetControllerFromHandle(
179 const Core::HID::SixAxisSensorHandle& device_handle); 187 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
180 const NpadControllerData& GetControllerFromHandle( 188 const NpadControllerData& GetControllerFromHandle(
181 const Core::HID::SixAxisSensorHandle& device_handle) const; 189 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
182 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 190 NpadControllerData& GetControllerFromNpadIdType(u64 aruid, Core::HID::NpadIdType npad_id);
183 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 191 const NpadControllerData& GetControllerFromNpadIdType(u64 aruid,
192 Core::HID::NpadIdType npad_id) const;
184 193
185 Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 194 Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
186 const Core::HID::SixAxisSensorHandle& device_handle); 195 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle);
187 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties( 196 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
188 const Core::HID::SixAxisSensorHandle& device_handle) const; 197 u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const;
189 198
190 std::atomic<u64> press_state{}; 199 Core::HID::HIDCore& hid_core;
191
192 std::array<NpadControllerData, NpadCount> controller_data{};
193 KernelHelpers::ServiceContext& service_context; 200 KernelHelpers::ServiceContext& service_context;
194 std::mutex mutex; 201
195 std::vector<Core::HID::NpadIdType> supported_npad_id_types{}; 202 s32 ref_counter{};
196 NpadJoyHoldType hold_type{NpadJoyHoldType::Vertical}; 203 mutable std::mutex mutex;
197 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 204 NPadResource npad_resource;
198 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 205 AppletResourceHolder applet_resource_holder{};
199 bool permit_vibration_session_enabled{false}; 206 Kernel::KEvent* input_event{nullptr};
200 bool analog_stick_use_center_clamp{false}; 207 std::mutex* input_mutex{nullptr};
201 bool is_in_lr_assignment_mode{false}; 208
202 bool is_controller_initialized{false}; 209 std::atomic<u64> press_state{};
210 bool permit_vibration_session_enabled;
211 std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax>
212 controller_data{};
203}; 213};
204} // namespace Service::HID 214} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_data.cpp b/src/core/hle/service/hid/controllers/npad/npad_data.cpp
new file mode 100644
index 000000000..d2423b6d3
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_data.cpp
@@ -0,0 +1,228 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/controllers/npad/npad_data.h"
5#include "core/hle/service/hid/hid_util.h"
6
7namespace Service::HID {
8
9NPadData::NPadData() {
10 ClearNpadSystemCommonPolicy();
11}
12
13NPadData::~NPadData() = default;
14
15NpadStatus NPadData::GetNpadStatus() const {
16 return status;
17}
18
19void NPadData::SetNpadAnalogStickUseCenterClamp(bool is_enabled) {
20 status.use_center_clamp.Assign(is_enabled);
21}
22
23bool NPadData::GetNpadAnalogStickUseCenterClamp() const {
24 return status.use_center_clamp.As<bool>();
25}
26
27void NPadData::SetNpadSystemExtStateEnabled(bool is_enabled) {
28 status.system_ext_state.Assign(is_enabled);
29}
30
31bool NPadData::GetNpadSystemExtState() const {
32 return status.system_ext_state.As<bool>();
33}
34
35Result NPadData::SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list) {
36 // Note: Real limit is 11. But array size is 10. N's bug?
37 if (list.size() > MaxSupportedNpadIdTypes) {
38 return ResultInvalidArraySize;
39 }
40
41 supported_npad_id_types_count = list.size();
42 memcpy(supported_npad_id_types.data(), list.data(),
43 list.size() * sizeof(Core::HID::NpadIdType));
44
45 return ResultSuccess;
46}
47
48std::size_t NPadData::GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const {
49 std::size_t out_size = std::min(supported_npad_id_types_count, out_list.size());
50
51 memcpy(out_list.data(), supported_npad_id_types.data(),
52 out_size * sizeof(Core::HID::NpadIdType));
53
54 return out_size;
55}
56
57bool NPadData::IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const {
58 for (std::size_t i = 0; i < supported_npad_id_types_count; i++) {
59 if (supported_npad_id_types[i] == npad_id) {
60 return true;
61 }
62 }
63
64 return false;
65}
66
67void NPadData::SetNpadSystemCommonPolicy(bool is_full_policy) {
68 supported_npad_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::JoyDual |
69 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
70 handheld_activation_mode = NpadHandheldActivationMode::Dual;
71
72 status.is_supported_styleset_set.Assign(true);
73 status.is_hold_type_set.Assign(true);
74 status.lr_assignment_mode.Assign(false);
75 status.is_policy.Assign(true);
76 if (is_full_policy) {
77 status.is_full_policy.Assign(true);
78 }
79
80 supported_npad_id_types_count = 10;
81 supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
82 supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
83 supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
84 supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
85 supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
86 supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
87 supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
88 supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
89 supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
90 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
91
92 for (auto& input_protection : is_unintended_home_button_input_protection) {
93 input_protection = true;
94 }
95}
96
97void NPadData::ClearNpadSystemCommonPolicy() {
98 status.raw = 0;
99 supported_npad_style_set = Core::HID::NpadStyleSet::All;
100 npad_hold_type = NpadJoyHoldType::Vertical;
101 handheld_activation_mode = NpadHandheldActivationMode::Dual;
102
103 for (auto& button_assignment : npad_button_assignment) {
104 button_assignment = Core::HID::NpadButton::None;
105 }
106
107 supported_npad_id_types_count = 10;
108 supported_npad_id_types[0] = Core::HID::NpadIdType::Player1;
109 supported_npad_id_types[1] = Core::HID::NpadIdType::Player2;
110 supported_npad_id_types[2] = Core::HID::NpadIdType::Player3;
111 supported_npad_id_types[3] = Core::HID::NpadIdType::Player4;
112 supported_npad_id_types[4] = Core::HID::NpadIdType::Player5;
113 supported_npad_id_types[5] = Core::HID::NpadIdType::Player6;
114 supported_npad_id_types[6] = Core::HID::NpadIdType::Player7;
115 supported_npad_id_types[7] = Core::HID::NpadIdType::Player8;
116 supported_npad_id_types[8] = Core::HID::NpadIdType::Other;
117 supported_npad_id_types[9] = Core::HID::NpadIdType::Handheld;
118
119 for (auto& input_protection : is_unintended_home_button_input_protection) {
120 input_protection = true;
121 }
122}
123
124void NPadData::SetNpadJoyHoldType(NpadJoyHoldType hold_type) {
125 npad_hold_type = hold_type;
126 status.is_hold_type_set.Assign(true);
127}
128
129NpadJoyHoldType NPadData::GetNpadJoyHoldType() const {
130 return npad_hold_type;
131}
132
133void NPadData::SetHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
134 handheld_activation_mode = activation_mode;
135}
136
137NpadHandheldActivationMode NPadData::GetHandheldActivationMode() const {
138 return handheld_activation_mode;
139}
140
141void NPadData::SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set) {
142 supported_npad_style_set = style_set;
143 status.is_supported_styleset_set.Assign(true);
144 status.is_hold_type_set.Assign(true);
145}
146
147Core::HID::NpadStyleSet NPadData::GetSupportedNpadStyleSet() const {
148 return supported_npad_style_set;
149}
150
151bool NPadData::IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const {
152 Core::HID::NpadStyleTag style = {supported_npad_style_set};
153 switch (style_index) {
154 case Core::HID::NpadStyleIndex::ProController:
155 return style.fullkey.As<bool>();
156 case Core::HID::NpadStyleIndex::Handheld:
157 return style.handheld.As<bool>();
158 case Core::HID::NpadStyleIndex::JoyconDual:
159 return style.joycon_dual.As<bool>();
160 case Core::HID::NpadStyleIndex::JoyconLeft:
161 return style.joycon_left.As<bool>();
162 case Core::HID::NpadStyleIndex::JoyconRight:
163 return style.joycon_right.As<bool>();
164 case Core::HID::NpadStyleIndex::GameCube:
165 return style.gamecube.As<bool>();
166 case Core::HID::NpadStyleIndex::Pokeball:
167 return style.palma.As<bool>();
168 case Core::HID::NpadStyleIndex::NES:
169 return style.lark.As<bool>();
170 case Core::HID::NpadStyleIndex::SNES:
171 return style.lucia.As<bool>();
172 case Core::HID::NpadStyleIndex::N64:
173 return style.lagoon.As<bool>();
174 case Core::HID::NpadStyleIndex::SegaGenesis:
175 return style.lager.As<bool>();
176 default:
177 return false;
178 }
179}
180
181void NPadData::SetLrAssignmentMode(bool is_enabled) {
182 status.lr_assignment_mode.Assign(is_enabled);
183}
184
185bool NPadData::GetLrAssignmentMode() const {
186 return status.lr_assignment_mode.As<bool>();
187}
188
189void NPadData::SetAssigningSingleOnSlSrPress(bool is_enabled) {
190 status.assigning_single_on_sl_sr_press.Assign(is_enabled);
191}
192
193bool NPadData::GetAssigningSingleOnSlSrPress() const {
194 return status.assigning_single_on_sl_sr_press.As<bool>();
195}
196
197void NPadData::SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id) {
198 is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)] = is_enabled;
199}
200
201bool NPadData::GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const {
202 return is_unintended_home_button_input_protection[NpadIdTypeToIndex(npad_id)];
203}
204
205void NPadData::SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
206 std::size_t style_index) {
207 npad_button_assignment[style_index] = button_assignment;
208}
209
210Core::HID::NpadButton NPadData::GetCaptureButtonAssignment(std::size_t style_index) const {
211 return npad_button_assignment[style_index];
212}
213
214std::size_t NPadData::GetNpadCaptureButtonAssignmentList(
215 std::span<Core::HID::NpadButton> out_list) const {
216 for (std::size_t i = 0; i < out_list.size(); i++) {
217 Core::HID::NpadStyleSet style_set = GetStylesetByIndex(i);
218 if ((style_set & supported_npad_style_set) == Core::HID::NpadStyleSet::None ||
219 npad_button_assignment[i] == Core::HID::NpadButton::None) {
220 return i;
221 }
222 out_list[i] = npad_button_assignment[i];
223 }
224
225 return out_list.size();
226}
227
228} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_data.h b/src/core/hle/service/hid/controllers/npad/npad_data.h
new file mode 100644
index 000000000..f799a9f9c
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_data.h
@@ -0,0 +1,88 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8
9#include "common/common_types.h"
10#include "core/hid/hid_types.h"
11#include "core/hle/result.h"
12#include "core/hle/service/hid/controllers/types/npad_types.h"
13
14namespace Service::HID {
15
16struct NpadStatus {
17 union {
18 u32 raw{};
19
20 BitField<0, 1, u32> is_supported_styleset_set;
21 BitField<1, 1, u32> is_hold_type_set;
22 BitField<2, 1, u32> lr_assignment_mode;
23 BitField<3, 1, u32> assigning_single_on_sl_sr_press;
24 BitField<4, 1, u32> is_full_policy;
25 BitField<5, 1, u32> is_policy;
26 BitField<6, 1, u32> use_center_clamp;
27 BitField<7, 1, u32> system_ext_state;
28 };
29};
30static_assert(sizeof(NpadStatus) == 4, "NpadStatus is an invalid size");
31
32/// Handles Npad request from HID interfaces
33class NPadData final {
34public:
35 explicit NPadData();
36 ~NPadData();
37
38 NpadStatus GetNpadStatus() const;
39
40 void SetNpadAnalogStickUseCenterClamp(bool is_enabled);
41 bool GetNpadAnalogStickUseCenterClamp() const;
42
43 void SetNpadSystemExtStateEnabled(bool is_enabled);
44 bool GetNpadSystemExtState() const;
45
46 Result SetSupportedNpadIdType(std::span<const Core::HID::NpadIdType> list);
47 std::size_t GetSupportedNpadIdType(std::span<Core::HID::NpadIdType> out_list) const;
48 bool IsNpadIdTypeSupported(Core::HID::NpadIdType npad_id) const;
49
50 void SetNpadSystemCommonPolicy(bool is_full_policy);
51 void ClearNpadSystemCommonPolicy();
52
53 void SetNpadJoyHoldType(NpadJoyHoldType hold_type);
54 NpadJoyHoldType GetNpadJoyHoldType() const;
55
56 void SetHandheldActivationMode(NpadHandheldActivationMode activation_mode);
57 NpadHandheldActivationMode GetHandheldActivationMode() const;
58
59 void SetSupportedNpadStyleSet(Core::HID::NpadStyleSet style_set);
60 Core::HID::NpadStyleSet GetSupportedNpadStyleSet() const;
61 bool IsNpadStyleIndexSupported(Core::HID::NpadStyleIndex style_index) const;
62
63 void SetLrAssignmentMode(bool is_enabled);
64 bool GetLrAssignmentMode() const;
65
66 void SetAssigningSingleOnSlSrPress(bool is_enabled);
67 bool GetAssigningSingleOnSlSrPress() const;
68
69 void SetHomeProtectionEnabled(bool is_enabled, Core::HID::NpadIdType npad_id);
70 bool GetHomeProtectionEnabled(Core::HID::NpadIdType npad_id) const;
71
72 void SetCaptureButtonAssignment(Core::HID::NpadButton button_assignment,
73 std::size_t style_index);
74 Core::HID::NpadButton GetCaptureButtonAssignment(std::size_t style_index) const;
75 std::size_t GetNpadCaptureButtonAssignmentList(std::span<Core::HID::NpadButton> out_list) const;
76
77private:
78 NpadStatus status{};
79 Core::HID::NpadStyleSet supported_npad_style_set{Core::HID::NpadStyleSet::All};
80 NpadJoyHoldType npad_hold_type{NpadJoyHoldType::Vertical};
81 NpadHandheldActivationMode handheld_activation_mode{};
82 std::array<Core::HID::NpadIdType, MaxSupportedNpadIdTypes> supported_npad_id_types{};
83 std::array<Core::HID::NpadButton, StyleIndexCount> npad_button_assignment{};
84 std::size_t supported_npad_id_types_count{};
85 std::array<bool, MaxSupportedNpadIdTypes> is_unintended_home_button_input_protection{};
86};
87
88} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_resource.cpp b/src/core/hle/service/hid/controllers/npad/npad_resource.cpp
new file mode 100644
index 000000000..0a9341a39
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_resource.cpp
@@ -0,0 +1,685 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/kernel/k_event.h"
5#include "core/hle/kernel/k_readable_event.h"
6#include "core/hle/service/hid/controllers/npad/npad_resource.h"
7#include "core/hle/service/hid/controllers/types/npad_types.h"
8#include "core/hle/service/hid/errors.h"
9#include "core/hle/service/hid/hid_util.h"
10
11namespace Service::HID {
12
13NPadResource::NPadResource(KernelHelpers::ServiceContext& context) : service_context{context} {}
14
15NPadResource::~NPadResource() = default;
16
17Result NPadResource::RegisterAppletResourceUserId(u64 aruid) {
18 const auto aruid_index = GetIndexFromAruid(aruid);
19 if (aruid_index < AruidIndexMax) {
20 return ResultAruidAlreadyRegistered;
21 }
22
23 std::size_t data_index = AruidIndexMax;
24 for (std::size_t i = 0; i < AruidIndexMax; i++) {
25 if (!state[i].flag.is_initialized) {
26 data_index = i;
27 break;
28 }
29 }
30
31 if (data_index == AruidIndexMax) {
32 return ResultAruidNoAvailableEntries;
33 }
34
35 auto& aruid_data = state[data_index];
36
37 aruid_data.aruid = aruid;
38 aruid_data.flag.is_initialized.Assign(true);
39
40 data_index = AruidIndexMax;
41 for (std::size_t i = 0; i < AruidIndexMax; i++) {
42 if (registration_list.flag[i] == RegistrationStatus::Initialized) {
43 if (registration_list.aruid[i] != aruid) {
44 continue;
45 }
46 data_index = i;
47 break;
48 }
49 if (registration_list.flag[i] == RegistrationStatus::None) {
50 data_index = i;
51 break;
52 }
53 }
54
55 if (data_index == AruidIndexMax) {
56 return ResultSuccess;
57 }
58
59 registration_list.flag[data_index] = RegistrationStatus::Initialized;
60 registration_list.aruid[data_index] = aruid;
61
62 return ResultSuccess;
63}
64
65void NPadResource::UnregisterAppletResourceUserId(u64 aruid) {
66 const u64 aruid_index = GetIndexFromAruid(aruid);
67
68 DestroyStyleSetUpdateEvents(aruid);
69 if (aruid_index < AruidIndexMax) {
70 state[aruid_index] = {};
71 registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete;
72 }
73}
74
75void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) {
76 const u64 aruid_index = GetIndexFromAruid(aruid);
77
78 if (aruid_index >= AruidIndexMax) {
79 return;
80 }
81
82 for (auto& controller_state : state[aruid_index].controller_state) {
83 if (!controller_state.is_styleset_update_event_initialized) {
84 continue;
85 }
86 service_context.CloseEvent(controller_state.style_set_update_event);
87 controller_state.is_styleset_update_event_initialized = false;
88 }
89}
90
91Result NPadResource::Activate(u64 aruid) {
92 const u64 aruid_index = GetIndexFromAruid(aruid);
93
94 if (aruid_index >= AruidIndexMax) {
95 return ResultSuccess;
96 }
97
98 auto& state_data = state[aruid_index];
99
100 if (state_data.flag.is_assigned) {
101 return ResultAruidAlreadyRegistered;
102 }
103
104 state_data.flag.is_assigned.Assign(true);
105 state_data.data.ClearNpadSystemCommonPolicy();
106 state_data.npad_revision = NpadRevision::Revision0;
107 state_data.button_config = {};
108
109 if (active_data_aruid == aruid) {
110 default_hold_type = active_data.GetNpadJoyHoldType();
111 active_data.SetNpadJoyHoldType(default_hold_type);
112 }
113 return ResultSuccess;
114}
115
116Result NPadResource::Activate() {
117 if (ref_counter == std::numeric_limits<s32>::max() - 1) {
118 return ResultAppletResourceOverflow;
119 }
120 if (ref_counter == 0) {
121 RegisterAppletResourceUserId(SystemAruid);
122 Activate(SystemAruid);
123 }
124 ref_counter++;
125 return ResultSuccess;
126}
127
128Result NPadResource::Deactivate() {
129 if (ref_counter == 0) {
130 return ResultAppletResourceNotInitialized;
131 }
132
133 UnregisterAppletResourceUserId(SystemAruid);
134 ref_counter--;
135 return ResultSuccess;
136}
137
138NPadData* NPadResource::GetActiveData() {
139 return &active_data;
140}
141
142u64 NPadResource::GetActiveDataAruid() {
143 return active_data_aruid;
144}
145
146void NPadResource::SetAppletResourceUserId(u64 aruid) {
147 if (active_data_aruid == aruid) {
148 return;
149 }
150
151 active_data_aruid = aruid;
152 default_hold_type = active_data.GetNpadJoyHoldType();
153 const u64 aruid_index = GetIndexFromAruid(aruid);
154
155 if (aruid_index >= AruidIndexMax) {
156 return;
157 }
158
159 auto& data = state[aruid_index].data;
160 if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
161 data.SetNpadJoyHoldType(default_hold_type);
162 }
163
164 active_data = data;
165 if (data.GetNpadStatus().is_hold_type_set) {
166 active_data.SetNpadJoyHoldType(default_hold_type);
167 }
168}
169
170std::size_t NPadResource::GetIndexFromAruid(u64 aruid) const {
171 for (std::size_t i = 0; i < AruidIndexMax; i++) {
172 if (registration_list.flag[i] == RegistrationStatus::Initialized &&
173 registration_list.aruid[i] == aruid) {
174 return i;
175 }
176 }
177 return AruidIndexMax;
178}
179
180Result NPadResource::ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy) {
181 const u64 aruid_index = GetIndexFromAruid(aruid);
182 if (aruid_index >= AruidIndexMax) {
183 return ResultNpadNotConnected;
184 }
185
186 auto& data = state[aruid_index].data;
187 data.SetNpadSystemCommonPolicy(is_full_policy);
188 data.SetNpadJoyHoldType(default_hold_type);
189 if (active_data_aruid == aruid) {
190 active_data.SetNpadSystemCommonPolicy(is_full_policy);
191 active_data.SetNpadJoyHoldType(default_hold_type);
192 }
193 return ResultSuccess;
194}
195
196Result NPadResource::ClearNpadSystemCommonPolicy(u64 aruid) {
197 const u64 aruid_index = GetIndexFromAruid(aruid);
198 if (aruid_index >= AruidIndexMax) {
199 return ResultNpadNotConnected;
200 }
201
202 state[aruid_index].data.ClearNpadSystemCommonPolicy();
203 if (active_data_aruid == aruid) {
204 active_data.ClearNpadSystemCommonPolicy();
205 }
206 return ResultSuccess;
207}
208
209Result NPadResource::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set) {
210 const u64 aruid_index = GetIndexFromAruid(aruid);
211 if (aruid_index >= AruidIndexMax) {
212 return ResultNpadNotConnected;
213 }
214
215 auto& data = state[aruid_index].data;
216 data.SetSupportedNpadStyleSet(style_set);
217 if (active_data_aruid == aruid) {
218 active_data.SetSupportedNpadStyleSet(style_set);
219 active_data.SetNpadJoyHoldType(data.GetNpadJoyHoldType());
220 }
221 return ResultSuccess;
222}
223
224Result NPadResource::GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set,
225 u64 aruid) const {
226 const u64 aruid_index = GetIndexFromAruid(aruid);
227 if (aruid_index >= AruidIndexMax) {
228 return ResultNpadNotConnected;
229 }
230
231 auto& data = state[aruid_index].data;
232 if (!data.GetNpadStatus().is_supported_styleset_set) {
233 return ResultUndefinedStyleset;
234 }
235
236 out_style_Set = data.GetSupportedNpadStyleSet();
237 return ResultSuccess;
238}
239
240Result NPadResource::GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set,
241 u64 aruid) const {
242 if (aruid == SystemAruid) {
243 out_style_set = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
244 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
245 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Palma |
246 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
247 return ResultSuccess;
248 }
249
250 const u64 aruid_index = GetIndexFromAruid(aruid);
251 if (aruid_index >= AruidIndexMax) {
252 return ResultNpadNotConnected;
253 }
254
255 auto& data = state[aruid_index].data;
256 if (!data.GetNpadStatus().is_supported_styleset_set) {
257 return ResultUndefinedStyleset;
258 }
259
260 Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
261 out_style_set = data.GetSupportedNpadStyleSet();
262
263 switch (state[aruid_index].npad_revision) {
264 case NpadRevision::Revision1:
265 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
266 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
267 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
268 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
269 Core::HID::NpadStyleSet::System;
270 break;
271 case NpadRevision::Revision2:
272 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
273 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
274 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
275 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
276 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
277 break;
278 case NpadRevision::Revision3:
279 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
280 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
281 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
282 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
283 Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
284 Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
285 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
286 break;
287 default:
288 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
289 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
290 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
291 Core::HID::NpadStyleSet::System;
292 break;
293 }
294
295 out_style_set = out_style_set & mask;
296 return ResultSuccess;
297}
298
299Result NPadResource::GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const {
300 const u64 aruid_index = GetIndexFromAruid(aruid);
301 if (aruid_index >= AruidIndexMax) {
302 return ResultNpadNotConnected;
303 }
304
305 auto& data = state[aruid_index].data;
306 if (!data.GetNpadStatus().is_supported_styleset_set) {
307 return ResultUndefinedStyleset;
308 }
309
310 Core::HID::NpadStyleSet mask{Core::HID::NpadStyleSet::None};
311 out_style_set = data.GetSupportedNpadStyleSet();
312
313 switch (state[aruid_index].npad_revision) {
314 case NpadRevision::Revision1:
315 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
316 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
317 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
318 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::SystemExt |
319 Core::HID::NpadStyleSet::System;
320 break;
321 case NpadRevision::Revision2:
322 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
323 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
324 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
325 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
326 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
327 break;
328 case NpadRevision::Revision3:
329 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
330 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
331 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::Gc |
332 Core::HID::NpadStyleSet::Palma | Core::HID::NpadStyleSet::Lark |
333 Core::HID::NpadStyleSet::HandheldLark | Core::HID::NpadStyleSet::Lucia |
334 Core::HID::NpadStyleSet::Lagoon | Core::HID::NpadStyleSet::Lager |
335 Core::HID::NpadStyleSet::SystemExt | Core::HID::NpadStyleSet::System;
336 break;
337 default:
338 mask = Core::HID::NpadStyleSet::Fullkey | Core::HID::NpadStyleSet::Handheld |
339 Core::HID::NpadStyleSet::JoyDual | Core::HID::NpadStyleSet::JoyLeft |
340 Core::HID::NpadStyleSet::JoyRight | Core::HID::NpadStyleSet::SystemExt |
341 Core::HID::NpadStyleSet::System;
342 break;
343 }
344
345 out_style_set = out_style_set & mask;
346 return ResultSuccess;
347}
348
349NpadRevision NPadResource::GetNpadRevision(u64 aruid) const {
350 const u64 aruid_index = GetIndexFromAruid(aruid);
351 if (aruid_index >= AruidIndexMax) {
352 return NpadRevision::Revision0;
353 }
354
355 return state[aruid_index].npad_revision;
356}
357
358Result NPadResource::IsSupportedNpadStyleSet(bool& is_set, u64 aruid) {
359 const u64 aruid_index = GetIndexFromAruid(aruid);
360 if (aruid_index >= AruidIndexMax) {
361 return ResultNpadNotConnected;
362 }
363
364 is_set = state[aruid_index].data.GetNpadStatus().is_supported_styleset_set.Value() != 0;
365 return ResultSuccess;
366}
367
368Result NPadResource::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
369 const u64 aruid_index = GetIndexFromAruid(aruid);
370 if (aruid_index >= AruidIndexMax) {
371 return ResultNpadNotConnected;
372 }
373
374 state[aruid_index].data.SetNpadJoyHoldType(hold_type);
375 if (active_data_aruid == aruid) {
376 active_data.SetNpadJoyHoldType(hold_type);
377 }
378 return ResultSuccess;
379}
380
381Result NPadResource::GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const {
382 const u64 aruid_index = GetIndexFromAruid(aruid);
383 if (aruid_index >= AruidIndexMax) {
384 return ResultNpadNotConnected;
385 }
386
387 auto& data = state[aruid_index].data;
388 if (data.GetNpadStatus().is_policy || data.GetNpadStatus().is_full_policy) {
389 hold_type = active_data.GetNpadJoyHoldType();
390 return ResultSuccess;
391 }
392 hold_type = data.GetNpadJoyHoldType();
393 return ResultSuccess;
394}
395
396Result NPadResource::SetNpadHandheldActivationMode(u64 aruid,
397 NpadHandheldActivationMode activation_mode) {
398 const u64 aruid_index = GetIndexFromAruid(aruid);
399 if (aruid_index >= AruidIndexMax) {
400 return ResultNpadNotConnected;
401 }
402
403 state[aruid_index].data.SetHandheldActivationMode(activation_mode);
404 if (active_data_aruid == aruid) {
405 active_data.SetHandheldActivationMode(activation_mode);
406 }
407 return ResultSuccess;
408}
409
410Result NPadResource::GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
411 u64 aruid) const {
412 const u64 aruid_index = GetIndexFromAruid(aruid);
413 if (aruid_index >= AruidIndexMax) {
414 return ResultNpadNotConnected;
415 }
416
417 activation_mode = state[aruid_index].data.GetHandheldActivationMode();
418 return ResultSuccess;
419}
420
421Result NPadResource::SetSupportedNpadIdType(
422 u64 aruid, std::span<const Core::HID::NpadIdType> supported_npad_list) {
423 const u64 aruid_index = GetIndexFromAruid(aruid);
424 if (aruid_index >= AruidIndexMax) {
425 return ResultNpadNotConnected;
426 }
427 if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
428 return ResultInvalidArraySize;
429 }
430
431 Result result = state[aruid_index].data.SetSupportedNpadIdType(supported_npad_list);
432 if (result.IsSuccess() && active_data_aruid == aruid) {
433 result = active_data.SetSupportedNpadIdType(supported_npad_list);
434 }
435
436 return result;
437}
438
439bool NPadResource::IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const {
440 const u64 aruid_index = GetIndexFromAruid(aruid);
441 if (aruid_index >= AruidIndexMax) {
442 return false;
443 }
444 return state[aruid_index].data.IsNpadStyleIndexSupported(style_index);
445}
446
447Result NPadResource::SetLrAssignmentMode(u64 aruid, bool is_enabled) {
448 const u64 aruid_index = GetIndexFromAruid(aruid);
449 if (aruid_index >= AruidIndexMax) {
450 return ResultNpadNotConnected;
451 }
452
453 state[aruid_index].data.SetLrAssignmentMode(is_enabled);
454 if (active_data_aruid == aruid) {
455 active_data.SetLrAssignmentMode(is_enabled);
456 }
457 return ResultSuccess;
458}
459
460Result NPadResource::GetLrAssignmentMode(bool& is_enabled, u64 aruid) const {
461 const u64 aruid_index = GetIndexFromAruid(aruid);
462 if (aruid_index >= AruidIndexMax) {
463 return ResultNpadNotConnected;
464 }
465
466 is_enabled = state[aruid_index].data.GetLrAssignmentMode();
467 return ResultSuccess;
468}
469
470Result NPadResource::SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) {
471 const u64 aruid_index = GetIndexFromAruid(aruid);
472 if (aruid_index >= AruidIndexMax) {
473 return ResultNpadNotConnected;
474 }
475
476 state[aruid_index].data.SetAssigningSingleOnSlSrPress(is_enabled);
477 if (active_data_aruid == aruid) {
478 active_data.SetAssigningSingleOnSlSrPress(is_enabled);
479 }
480 return ResultSuccess;
481}
482
483Result NPadResource::IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const {
484 const u64 aruid_index = GetIndexFromAruid(aruid);
485 if (aruid_index >= AruidIndexMax) {
486 return ResultNpadNotConnected;
487 }
488
489 is_enabled = state[aruid_index].data.GetAssigningSingleOnSlSrPress();
490 return ResultSuccess;
491}
492
493Result NPadResource::AcquireNpadStyleSetUpdateEventHandle(u64 aruid,
494 Kernel::KReadableEvent** out_event,
495 Core::HID::NpadIdType npad_id) {
496 const u64 aruid_index = GetIndexFromAruid(aruid);
497 if (aruid_index >= AruidIndexMax) {
498 return ResultNpadNotConnected;
499 }
500
501 auto& controller_state = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
502 if (!controller_state.is_styleset_update_event_initialized) {
503 // Auto clear = true
504 controller_state.style_set_update_event =
505 service_context.CreateEvent("NpadResource:StylesetUpdateEvent");
506
507 // Assume creating the event succeeds otherwise crash the system here
508 controller_state.is_styleset_update_event_initialized = true;
509 }
510
511 *out_event = &controller_state.style_set_update_event->GetReadableEvent();
512
513 if (controller_state.is_styleset_update_event_initialized) {
514 controller_state.style_set_update_event->Signal();
515 }
516
517 return ResultSuccess;
518}
519
520Result NPadResource::SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id) {
521 const u64 aruid_index = GetIndexFromAruid(aruid);
522 if (aruid_index >= AruidIndexMax) {
523 return ResultNpadNotConnected;
524 }
525 auto controller = state[aruid_index].controller_state[NpadIdTypeToIndex(npad_id)];
526 if (controller.is_styleset_update_event_initialized) {
527 controller.style_set_update_event->Signal();
528 }
529 return ResultSuccess;
530}
531
532Result NPadResource::GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
533 Core::HID::NpadIdType npad_id) const {
534 const u64 aruid_index = GetIndexFromAruid(aruid);
535 if (aruid_index >= AruidIndexMax) {
536 return ResultNpadNotConnected;
537 }
538
539 is_enabled = state[aruid_index].data.GetHomeProtectionEnabled(npad_id);
540 return ResultSuccess;
541}
542
543Result NPadResource::SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id,
544 bool is_enabled) {
545 const u64 aruid_index = GetIndexFromAruid(aruid);
546 if (aruid_index >= AruidIndexMax) {
547 return ResultNpadNotConnected;
548 }
549
550 state[aruid_index].data.SetHomeProtectionEnabled(is_enabled, npad_id);
551 if (active_data_aruid == aruid) {
552 active_data.SetHomeProtectionEnabled(is_enabled, npad_id);
553 }
554 return ResultSuccess;
555}
556
557Result NPadResource::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
558 const u64 aruid_index = GetIndexFromAruid(aruid);
559 if (aruid_index >= AruidIndexMax) {
560 return ResultNpadNotConnected;
561 }
562
563 state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
564 if (active_data_aruid == aruid) {
565 active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
566 }
567 return ResultSuccess;
568}
569
570Result NPadResource::SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
571 Core::HID::NpadButton button_config) {
572 const u64 aruid_index = GetIndexFromAruid(aruid);
573 if (aruid_index >= AruidIndexMax) {
574 return ResultNpadNotConnected;
575 }
576
577 state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index] = button_config;
578 return ResultSuccess;
579}
580
581Core::HID::NpadButton NPadResource::GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
582 std::size_t index, Core::HID::NpadButton mask,
583 bool is_enabled) {
584 const u64 aruid_index = GetIndexFromAruid(aruid);
585 if (aruid_index >= AruidIndexMax) {
586 return Core::HID::NpadButton::None;
587 }
588
589 auto& button_config = state[aruid_index].button_config[NpadIdTypeToIndex(npad_id)][index];
590 if (is_enabled) {
591 button_config = button_config | mask;
592 return button_config;
593 }
594
595 button_config = Core::HID::NpadButton::None;
596 return Core::HID::NpadButton::None;
597}
598
599void NPadResource::ResetButtonConfig() {
600 for (auto& selected_state : state) {
601 selected_state.button_config = {};
602 }
603}
604
605Result NPadResource::SetNpadCaptureButtonAssignment(u64 aruid,
606 Core::HID::NpadStyleSet npad_style_set,
607 Core::HID::NpadButton button_assignment) {
608 const u64 aruid_index = GetIndexFromAruid(aruid);
609 if (aruid_index >= AruidIndexMax) {
610 return ResultNpadNotConnected;
611 }
612
613 // Must be a power of two
614 const auto raw_styleset = static_cast<u32>(npad_style_set);
615 if (raw_styleset == 0 && (raw_styleset & (raw_styleset - 1)) != 0) {
616 return ResultMultipleStyleSetSelected;
617 }
618
619 std::size_t style_index{};
620 Core::HID::NpadStyleSet style_selected{};
621 for (style_index = 0; style_index < StyleIndexCount; ++style_index) {
622 style_selected = GetStylesetByIndex(style_index);
623 if (npad_style_set == style_selected) {
624 break;
625 }
626 }
627
628 if (style_selected == Core::HID::NpadStyleSet::None) {
629 return ResultMultipleStyleSetSelected;
630 }
631
632 state[aruid_index].data.SetCaptureButtonAssignment(button_assignment, style_index);
633 if (active_data_aruid == aruid) {
634 active_data.SetCaptureButtonAssignment(button_assignment, style_index);
635 }
636 return ResultSuccess;
637}
638
639Result NPadResource::ClearNpadCaptureButtonAssignment(u64 aruid) {
640 const u64 aruid_index = GetIndexFromAruid(aruid);
641 if (aruid_index >= AruidIndexMax) {
642 return ResultNpadNotConnected;
643 }
644
645 for (std::size_t i = 0; i < StyleIndexCount; i++) {
646 state[aruid_index].data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
647 if (active_data_aruid == aruid) {
648 active_data.SetCaptureButtonAssignment(Core::HID::NpadButton::None, i);
649 }
650 }
651 return ResultSuccess;
652}
653
654std::size_t NPadResource::GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
655 u64 aruid) const {
656 const u64 aruid_index = GetIndexFromAruid(aruid);
657 if (aruid_index >= AruidIndexMax) {
658 return 0;
659 }
660 return state[aruid_index].data.GetNpadCaptureButtonAssignmentList(out_list);
661}
662
663void NPadResource::SetNpadRevision(u64 aruid, NpadRevision revision) {
664 const u64 aruid_index = GetIndexFromAruid(aruid);
665 if (aruid_index >= AruidIndexMax) {
666 return;
667 }
668
669 state[aruid_index].npad_revision = revision;
670}
671
672Result NPadResource::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) {
673 const u64 aruid_index = GetIndexFromAruid(aruid);
674 if (aruid_index >= AruidIndexMax) {
675 return ResultNpadNotConnected;
676 }
677
678 state[aruid_index].data.SetNpadAnalogStickUseCenterClamp(is_enabled);
679 if (active_data_aruid == aruid) {
680 active_data.SetNpadAnalogStickUseCenterClamp(is_enabled);
681 }
682 return ResultSuccess;
683}
684
685} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad/npad_resource.h b/src/core/hle/service/hid/controllers/npad/npad_resource.h
new file mode 100644
index 000000000..4c7e6ab0e
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/npad/npad_resource.h
@@ -0,0 +1,132 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include <array>
7#include <mutex>
8#include <span>
9
10#include "common/common_types.h"
11#include "core/hid/hid_types.h"
12#include "core/hle/result.h"
13#include "core/hle/service/hid/controllers/applet_resource.h"
14#include "core/hle/service/hid/controllers/npad/npad_data.h"
15#include "core/hle/service/hid/controllers/types/npad_types.h"
16#include "core/hle/service/kernel_helpers.h"
17
18namespace Core {
19class System;
20}
21
22namespace Kernel {
23class KReadableEvent;
24}
25
26namespace Service::HID {
27struct DataStatusFlag;
28
29struct NpadControllerState {
30 bool is_styleset_update_event_initialized{};
31 INSERT_PADDING_BYTES(0x7);
32 Kernel::KEvent* style_set_update_event{nullptr};
33 INSERT_PADDING_BYTES(0x27);
34};
35
36struct NpadState {
37 DataStatusFlag flag{};
38 u64 aruid{};
39 NPadData data{};
40 std::array<std::array<Core::HID::NpadButton, StyleIndexCount>, MaxSupportedNpadIdTypes>
41 button_config;
42 std::array<NpadControllerState, MaxSupportedNpadIdTypes> controller_state;
43 NpadRevision npad_revision;
44};
45
46/// Handles Npad request from HID interfaces
47class NPadResource final {
48public:
49 explicit NPadResource(KernelHelpers::ServiceContext& context);
50 ~NPadResource();
51
52 NPadData* GetActiveData();
53 u64 GetActiveDataAruid();
54
55 Result RegisterAppletResourceUserId(u64 aruid);
56 void UnregisterAppletResourceUserId(u64 aruid);
57
58 void DestroyStyleSetUpdateEvents(u64 aruid);
59
60 Result Activate(u64 aruid);
61 Result Activate();
62 Result Deactivate();
63
64 void SetAppletResourceUserId(u64 aruid);
65 std::size_t GetIndexFromAruid(u64 aruid) const;
66
67 Result ApplyNpadSystemCommonPolicy(u64 aruid, bool is_full_policy);
68 Result ClearNpadSystemCommonPolicy(u64 aruid);
69
70 Result SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet style_set);
71 Result GetSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_Set, u64 aruid) const;
72 Result GetMaskedSupportedNpadStyleSet(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;
73 Result GetAvailableStyleset(Core::HID::NpadStyleSet& out_style_set, u64 aruid) const;
74
75 NpadRevision GetNpadRevision(u64 aruid) const;
76 void SetNpadRevision(u64 aruid, NpadRevision revision);
77
78 Result IsSupportedNpadStyleSet(bool& is_set, u64 aruid);
79
80 Result SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type);
81 Result GetNpadJoyHoldType(NpadJoyHoldType& hold_type, u64 aruid) const;
82
83 Result SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode activation_mode);
84 Result GetNpadHandheldActivationMode(NpadHandheldActivationMode& activation_mode,
85 u64 aruid) const;
86
87 Result SetSupportedNpadIdType(u64 aruid,
88 std::span<const Core::HID::NpadIdType> supported_npad_list);
89 bool IsControllerSupported(u64 aruid, Core::HID::NpadStyleIndex style_index) const;
90
91 Result SetLrAssignmentMode(u64 aruid, bool is_enabled);
92 Result GetLrAssignmentMode(bool& is_enabled, u64 aruid) const;
93
94 Result SetAssigningSingleOnSlSrPress(u64 aruid, bool is_enabled);
95 Result IsAssigningSingleOnSlSrPressEnabled(bool& is_enabled, u64 aruid) const;
96
97 Result AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
98 Core::HID::NpadIdType npad_id);
99 Result SignalStyleSetUpdateEvent(u64 aruid, Core::HID::NpadIdType npad_id);
100
101 Result GetHomeProtectionEnabled(bool& is_enabled, u64 aruid,
102 Core::HID::NpadIdType npad_id) const;
103 Result SetHomeProtectionEnabled(u64 aruid, Core::HID::NpadIdType npad_id, bool is_enabled);
104
105 Result SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled);
106
107 Result SetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id, std::size_t index,
108 Core::HID::NpadButton button_config);
109 Core::HID::NpadButton GetButtonConfig(u64 aruid, Core::HID::NpadIdType npad_id,
110 std::size_t index, Core::HID::NpadButton mask,
111 bool is_enabled);
112 void ResetButtonConfig();
113
114 Result SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set,
115 Core::HID::NpadButton button_assignment);
116 Result ClearNpadCaptureButtonAssignment(u64 aruid);
117 std::size_t GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
118 u64 aruid) const;
119
120 Result SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled);
121
122private:
123 NPadData active_data{};
124 AruidRegisterList registration_list{};
125 std::array<NpadState, AruidIndexMax> state{};
126 u64 active_data_aruid{};
127 NpadJoyHoldType default_hold_type{};
128 s32 ref_counter{};
129
130 KernelHelpers::ServiceContext& service_context;
131};
132} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
index 51581188e..0bc5169c6 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
+++ b/src/core/hle/service/hid/controllers/shared_memory_holder.cpp
@@ -3,8 +3,9 @@
3 3
4#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_shared_memory.h" 5#include "core/hle/kernel/k_shared_memory.h"
6#include "core/hle/service/hid/controllers/shared_memory_format.h" 6#include "core/hle/service/hid/controllers/applet_resource.h"
7#include "core/hle/service/hid/controllers/shared_memory_holder.h" 7#include "core/hle/service/hid/controllers/shared_memory_holder.h"
8#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8#include "core/hle/service/hid/errors.h" 9#include "core/hle/service/hid/errors.h"
9 10
10namespace Service::HID { 11namespace Service::HID {
diff --git a/src/core/hle/service/hid/controllers/six_axis.cpp b/src/core/hle/service/hid/controllers/six_axis.cpp
index 36b72f9ea..adab60911 100644
--- a/src/core/hle/service/hid/controllers/six_axis.cpp
+++ b/src/core/hle/service/hid/controllers/six_axis.cpp
@@ -6,8 +6,8 @@
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 "core/hle/service/hid/controllers/npad.h" 8#include "core/hle/service/hid/controllers/npad.h"
9#include "core/hle/service/hid/controllers/shared_memory_format.h"
10#include "core/hle/service/hid/controllers/six_axis.h" 9#include "core/hle/service/hid/controllers/six_axis.h"
10#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
11#include "core/hle/service/hid/errors.h" 11#include "core/hle/service/hid/errors.h"
12#include "core/hle/service/hid/hid_util.h" 12#include "core/hle/service/hid/hid_util.h"
13 13
@@ -27,14 +27,21 @@ void SixAxis::OnInit() {}
27void SixAxis::OnRelease() {} 27void SixAxis::OnRelease() {}
28 28
29void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 29void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 std::scoped_lock shared_lock{*shared_mutex};
31 const u64 aruid = applet_resource->GetActiveAruid();
32 auto* data = applet_resource->GetAruidData(aruid);
33
34 if (data == nullptr || !data->flag.is_assigned) {
35 return;
36 }
37
30 if (!IsControllerActivated()) { 38 if (!IsControllerActivated()) {
31 return; 39 return;
32 } 40 }
33 41
34 for (std::size_t i = 0; i < controller_data.size(); ++i) { 42 for (std::size_t i = 0; i < controller_data.size(); ++i) {
43 NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i];
35 auto& controller = controller_data[i]; 44 auto& controller = controller_data[i];
36
37 const auto npad_id = IndexToNpadIdType(i);
38 const auto& controller_type = controller.device->GetNpadStyleIndex(); 45 const auto& controller_type = controller.device->GetNpadStyleIndex();
39 46
40 if (controller_type == Core::HID::NpadStyleIndex::None || 47 if (controller_type == Core::HID::NpadStyleIndex::None ||
@@ -50,12 +57,12 @@ void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
50 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; 57 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
51 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; 58 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
52 59
53 auto& sixaxis_fullkey_lifo = npad->GetSixAxisFullkeyLifo(npad_id); 60 auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo;
54 auto& sixaxis_handheld_lifo = npad->GetSixAxisHandheldLifo(npad_id); 61 auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo;
55 auto& sixaxis_dual_left_lifo = npad->GetSixAxisDualLeftLifo(npad_id); 62 auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo;
56 auto& sixaxis_dual_right_lifo = npad->GetSixAxisDualRightLifo(npad_id); 63 auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo;
57 auto& sixaxis_left_lifo = npad->GetSixAxisLeftLifo(npad_id); 64 auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo;
58 auto& sixaxis_right_lifo = npad->GetSixAxisRightLifo(npad_id); 65 auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo;
59 66
60 // Clear previous state 67 // Clear previous state
61 sixaxis_fullkey_state = {}; 68 sixaxis_fullkey_state = {};
diff --git a/src/core/hle/service/hid/controllers/sleep_button.cpp b/src/core/hle/service/hid/controllers/sleep_button.cpp
new file mode 100644
index 000000000..d44b1f4cc
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.cpp
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/sleep_button.h"
7#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
8
9namespace Service::HID {
10
11SleepButton::SleepButton(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13SleepButton::~SleepButton() = default;
14
15void SleepButton::OnInit() {}
16
17void SleepButton::OnRelease() {}
18
19void SleepButton::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 std::scoped_lock shared_lock{*shared_mutex};
25 const u64 aruid = applet_resource->GetActiveAruid();
26 auto* data = applet_resource->GetAruidData(aruid);
27
28 if (data == nullptr || !data->flag.is_assigned) {
29 return;
30 }
31
32 auto& header = data->shared_memory_format->capture_button.header;
33 header.timestamp = core_timing.GetGlobalTimeNs().count();
34 header.total_entry_count = 17;
35 header.entry_count = 0;
36 header.last_entry_index = 0;
37}
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/sleep_button.h b/src/core/hle/service/hid/controllers/sleep_button.h
new file mode 100644
index 000000000..59964bf63
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/sleep_button.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class SleepButton final : public ControllerBase {
11public:
12 explicit SleepButton(Core::HID::HIDCore& hid_core_);
13 ~SleepButton() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
deleted file mode 100644
index e2a5f5d79..000000000
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/shared_memory_format.h"
6#include "core/hle/service/hid/controllers/stubbed.h"
7
8namespace Service::HID {
9
10Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_,
11 CommonHeader& ring_lifo_header)
12 : ControllerBase{hid_core_}, header{ring_lifo_header} {}
13
14Controller_Stubbed::~Controller_Stubbed() = default;
15
16void Controller_Stubbed::OnInit() {}
17
18void Controller_Stubbed::OnRelease() {}
19
20void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
21 if (!smart_update) {
22 return;
23 }
24
25 header.timestamp = core_timing.GetGlobalTimeNs().count();
26 header.total_entry_count = 17;
27 header.entry_count = 0;
28 header.last_entry_index = 0;
29}
30
31} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 469750006..b585a5829 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -8,15 +8,14 @@
8#include "core/frontend/emu_window.h" 8#include "core/frontend/emu_window.h"
9#include "core/hid/emulated_console.h" 9#include "core/hid/emulated_console.h"
10#include "core/hid/hid_core.h" 10#include "core/hid/hid_core.h"
11#include "core/hle/service/hid/controllers/shared_memory_format.h" 11#include "core/hle/service/hid/controllers/applet_resource.h"
12#include "core/hle/service/hid/controllers/touchscreen.h" 12#include "core/hle/service/hid/controllers/touchscreen.h"
13#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
13 14
14namespace Service::HID { 15namespace Service::HID {
15 16
16TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_, 17TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_)
17 TouchScreenSharedMemoryFormat& touch_shared_memory) 18 : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width),
18 : ControllerBase{hid_core_}, shared_memory{touch_shared_memory},
19 touchscreen_width(Layout::ScreenUndocked::Width),
20 touchscreen_height(Layout::ScreenUndocked::Height) { 19 touchscreen_height(Layout::ScreenUndocked::Height) {
21 console = hid_core.GetEmulatedConsole(); 20 console = hid_core.GetEmulatedConsole();
22} 21}
@@ -28,6 +27,14 @@ void TouchScreen::OnInit() {}
28void TouchScreen::OnRelease() {} 27void TouchScreen::OnRelease() {}
29 28
30void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { 29void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
30 const u64 aruid = applet_resource->GetActiveAruid();
31 auto* data = applet_resource->GetAruidData(aruid);
32
33 if (data == nullptr || !data->flag.is_assigned) {
34 return;
35 }
36
37 TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen;
31 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); 38 shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count();
32 39
33 if (!IsControllerActivated()) { 40 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 5b6305bfc..945d359be 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -18,8 +18,7 @@ struct TouchScreenSharedMemoryFormat;
18 18
19class TouchScreen final : public ControllerBase { 19class TouchScreen final : public ControllerBase {
20public: 20public:
21 explicit TouchScreen(Core::HID::HIDCore& hid_core_, 21 explicit TouchScreen(Core::HID::HIDCore& hid_core_);
22 TouchScreenSharedMemoryFormat& touch_shared_memory);
23 ~TouchScreen() override; 22 ~TouchScreen() override;
24 23
25 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -35,7 +34,6 @@ public:
35 34
36private: 35private:
37 TouchScreenState next_state{}; 36 TouchScreenState next_state{};
38 TouchScreenSharedMemoryFormat& shared_memory;
39 Core::HID::EmulatedConsole* console = nullptr; 37 Core::HID::EmulatedConsole* console = nullptr;
40 38
41 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; 39 std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
diff --git a/src/core/hle/service/hid/controllers/types/npad_types.h b/src/core/hle/service/hid/controllers/types/npad_types.h
index a5ce2562b..419c33a8c 100644
--- a/src/core/hle/service/hid/controllers/types/npad_types.h
+++ b/src/core/hle/service/hid/controllers/types/npad_types.h
@@ -9,7 +9,8 @@
9#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
10 10
11namespace Service::HID { 11namespace Service::HID {
12static constexpr std::size_t NpadCount = 10; 12static constexpr std::size_t MaxSupportedNpadIdTypes = 10;
13static constexpr std::size_t StyleIndexCount = 7;
13 14
14// This is nn::hid::NpadJoyHoldType 15// This is nn::hid::NpadJoyHoldType
15enum class NpadJoyHoldType : u64 { 16enum class NpadJoyHoldType : u64 {
diff --git a/src/core/hle/service/hid/controllers/shared_memory_format.h b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
index 2986c113e..976043b9c 100644
--- a/src/core/hle/service/hid/controllers/shared_memory_format.h
+++ b/src/core/hle/service/hid/controllers/types/shared_memory_format.h
@@ -171,7 +171,7 @@ static_assert(sizeof(NpadSharedMemoryEntry) == 0x5000, "NpadSharedMemoryEntry is
171 171
172// This is nn::hid::detail::NpadSharedMemoryFormat 172// This is nn::hid::detail::NpadSharedMemoryFormat
173struct NpadSharedMemoryFormat { 173struct NpadSharedMemoryFormat {
174 std::array<NpadSharedMemoryEntry, NpadCount> npad_entry; 174 std::array<NpadSharedMemoryEntry, MaxSupportedNpadIdTypes> npad_entry;
175}; 175};
176static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000, 176static_assert(sizeof(NpadSharedMemoryFormat) == 0x32000,
177 "NpadSharedMemoryFormat is an invalid size"); 177 "NpadSharedMemoryFormat is an invalid size");
diff --git a/src/core/hle/service/hid/controllers/unique_pad.cpp b/src/core/hle/service/hid/controllers/unique_pad.cpp
new file mode 100644
index 000000000..6c543031d
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.cpp
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core_timing.h"
5#include "core/hle/service/hid/controllers/applet_resource.h"
6#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
7#include "core/hle/service/hid/controllers/unique_pad.h"
8
9namespace Service::HID {
10
11UniquePad::UniquePad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
12
13UniquePad::~UniquePad() = default;
14
15void UniquePad::OnInit() {}
16
17void UniquePad::OnRelease() {}
18
19void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
20 if (!smart_update) {
21 return;
22 }
23
24 const u64 aruid = applet_resource->GetActiveAruid();
25 auto* data = applet_resource->GetAruidData(aruid);
26
27 if (data == nullptr || !data->flag.is_assigned) {
28 return;
29 }
30
31 auto& header = data->shared_memory_format->capture_button.header;
32 header.timestamp = core_timing.GetGlobalTimeNs().count();
33 header.total_entry_count = 17;
34 header.entry_count = 0;
35 header.last_entry_index = 0;
36}
37
38} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/unique_pad.h b/src/core/hle/service/hid/controllers/unique_pad.h
new file mode 100644
index 000000000..966368264
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/unique_pad.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/hid/controllers/controller_base.h"
7
8namespace Service::HID {
9
10class UniquePad final : public ControllerBase {
11public:
12 explicit UniquePad(Core::HID::HIDCore& hid_core_);
13 ~UniquePad() override;
14
15 // Called when the controller is initialized
16 void OnInit() override;
17
18 // When the controller is released
19 void OnRelease() override;
20
21 // When the controller is requesting an update for the shared memory
22 void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
23
24private:
25 bool smart_update{};
26};
27} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 6dc976fe1..bb14aa61e 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -10,15 +10,32 @@ namespace Service::HID {
10constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; 10constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
11constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; 11constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
12constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; 12constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
13constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122}; 13
14constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123}; 14constexpr Result ResultVibrationNotInitialized{ErrorModule::HID, 121};
15constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124}; 15constexpr Result ResultVibrationInvalidStyleIndex{ErrorModule::HID, 122};
16constexpr Result ResultVibrationInvalidNpadId{ErrorModule::HID, 123};
17constexpr Result ResultVibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
18constexpr Result ResultVibrationStrenghtOutOfRange{ErrorModule::HID, 126};
19constexpr Result ResultVibrationArraySizeMismatch{ErrorModule::HID, 131};
20
16constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; 21constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
22
23constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461};
24constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464};
25constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501};
26constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541};
27
17constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; 28constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
18constexpr Result NpadIsSameType{ErrorModule::HID, 602}; 29constexpr Result NpadIsSameType{ErrorModule::HID, 602};
19constexpr Result InvalidNpadId{ErrorModule::HID, 709}; 30constexpr Result ResultNpadIsNotProController{ErrorModule::HID, 604};
20constexpr Result NpadNotConnected{ErrorModule::HID, 710}; 31
21constexpr Result InvalidArraySize{ErrorModule::HID, 715}; 32constexpr Result ResultInvalidNpadId{ErrorModule::HID, 709};
33constexpr Result ResultNpadNotConnected{ErrorModule::HID, 710};
34constexpr Result ResultNpadHandlerOverflow{ErrorModule::HID, 711};
35constexpr Result ResultNpadHandlerNotInitialized{ErrorModule::HID, 712};
36constexpr Result ResultInvalidArraySize{ErrorModule::HID, 715};
37constexpr Result ResultUndefinedStyleset{ErrorModule::HID, 716};
38constexpr Result ResultMultipleStyleSetSelected{ErrorModule::HID, 717};
22 39
23constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041}; 40constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041};
24constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042}; 41constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042};
@@ -27,6 +44,9 @@ constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
27constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046}; 44constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
28constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047}; 45constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};
29 46
47constexpr Result ResultNpadResourceOverflow{ErrorModule::HID, 2001};
48constexpr Result ResultNpadResourceNotInitialized{ErrorModule::HID, 2002};
49
30constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; 50constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
31 51
32} // namespace Service::HID 52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index afbcb019f..bd2873181 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -25,6 +25,7 @@ void LoopProcess(Core::System& system) {
25 // TODO: Remove this hack until this service is emulated properly. 25 // TODO: Remove this hack until this service is emulated properly.
26 const auto process_list = system.Kernel().GetProcessList(); 26 const auto process_list = system.Kernel().GetProcessList();
27 if (!process_list.empty()) { 27 if (!process_list.empty()) {
28 resouce_manager->Initialize();
28 resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true); 29 resouce_manager->RegisterAppletResourceUserId(process_list[0]->GetId(), true);
29 } 30 }
30 31
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index de24b0401..a953c92b3 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -51,7 +51,7 @@ private:
51 IPC::RequestParser rp{ctx}; 51 IPC::RequestParser rp{ctx};
52 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 52 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
53 53
54 if (resource_manager != nullptr) { 54 if (resource_manager != nullptr && resource_manager->GetNpad()) {
55 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); 55 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
56 } 56 }
57 57
@@ -785,8 +785,8 @@ void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ct
785 785
786 bool is_firmware_available{}; 786 bool is_firmware_available{};
787 auto controller = GetResourceManager()->GetNpad(); 787 auto controller = GetResourceManager()->GetNpad();
788 controller->IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, 788 controller->IsFirmwareUpdateAvailableForSixAxisSensor(
789 is_firmware_available); 789 parameters.applet_resource_user_id, parameters.sixaxis_handle, is_firmware_available);
790 790
791 LOG_WARNING( 791 LOG_WARNING(
792 Service_HID, 792 Service_HID,
@@ -924,8 +924,8 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx)
924 const auto parameters{rp.PopRaw<Parameters>()}; 924 const auto parameters{rp.PopRaw<Parameters>()};
925 925
926 auto controller = GetResourceManager()->GetNpad(); 926 auto controller = GetResourceManager()->GetNpad();
927 const auto result = 927 const auto result = controller->ResetIsSixAxisSensorDeviceNewlyAssigned(
928 controller->ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); 928 parameters.applet_resource_user_id, parameters.sixaxis_handle);
929 929
930 LOG_WARNING( 930 LOG_WARNING(
931 Service_HID, 931 Service_HID,
@@ -970,7 +970,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
970void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { 970void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
971 IPC::RequestParser rp{ctx}; 971 IPC::RequestParser rp{ctx};
972 struct Parameters { 972 struct Parameters {
973 Core::HID::NpadStyleSet supported_styleset; 973 Core::HID::NpadStyleSet supported_style_set;
974 INSERT_PADDING_WORDS_NOINIT(1); 974 INSERT_PADDING_WORDS_NOINIT(1);
975 u64 applet_resource_user_id; 975 u64 applet_resource_user_id;
976 }; 976 };
@@ -978,13 +978,25 @@ void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
978 978
979 const auto parameters{rp.PopRaw<Parameters>()}; 979 const auto parameters{rp.PopRaw<Parameters>()};
980 980
981 GetResourceManager()->GetNpad()->SetSupportedStyleSet({parameters.supported_styleset}); 981 LOG_DEBUG(Service_HID, "called, supported_style_set={}, applet_resource_user_id={}",
982 parameters.supported_style_set, parameters.applet_resource_user_id);
983
984 const auto npad = GetResourceManager()->GetNpad();
985 const Result result = npad->SetSupportedNpadStyleSet(parameters.applet_resource_user_id,
986 parameters.supported_style_set);
982 987
983 LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", 988 if (result.IsSuccess()) {
984 parameters.supported_styleset, parameters.applet_resource_user_id); 989 Core::HID::NpadStyleTag style_tag{parameters.supported_style_set};
990 const auto revision = npad->GetRevision(parameters.applet_resource_user_id);
991
992 if (style_tag.palma != 0 && revision < NpadRevision::Revision3) {
993 // GetResourceManager()->GetPalma()->EnableBoostMode(parameters.applet_resource_user_id,
994 // true);
995 }
996 }
985 997
986 IPC::ResponseBuilder rb{ctx, 2}; 998 IPC::ResponseBuilder rb{ctx, 2};
987 rb.Push(ResultSuccess); 999 rb.Push(result);
988} 1000}
989 1001
990void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { 1002void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
@@ -993,19 +1005,31 @@ void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) {
993 1005
994 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1006 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
995 1007
1008 Core::HID::NpadStyleSet supported_style_set{};
1009 const auto npad = GetResourceManager()->GetNpad();
1010 const auto result =
1011 npad->GetSupportedNpadStyleSet(applet_resource_user_id, supported_style_set);
1012
996 IPC::ResponseBuilder rb{ctx, 3}; 1013 IPC::ResponseBuilder rb{ctx, 3};
997 rb.Push(ResultSuccess); 1014 rb.Push(result);
998 rb.PushEnum(GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw); 1015 rb.PushEnum(supported_style_set);
999} 1016}
1000 1017
1001void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { 1018void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) {
1002 IPC::RequestParser rp{ctx}; 1019 IPC::RequestParser rp{ctx};
1003 const auto applet_resource_user_id{rp.Pop<u64>()}; 1020 const auto applet_resource_user_id{rp.Pop<u64>()};
1004 1021 const auto buffer = ctx.ReadBuffer();
1005 const auto result = GetResourceManager()->GetNpad()->SetSupportedNpadIdTypes(ctx.ReadBuffer()); 1022 const std::size_t elements = ctx.GetReadBufferNumElements<Core::HID::NpadIdType>();
1006 1023
1007 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1024 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1008 1025
1026 std::vector<Core::HID::NpadIdType> supported_npad_list(elements);
1027 memcpy(supported_npad_list.data(), buffer.data(), buffer.size());
1028
1029 const auto npad = GetResourceManager()->GetNpad();
1030 const Result result =
1031 npad->SetSupportedNpadIdType(applet_resource_user_id, supported_npad_list);
1032
1009 IPC::ResponseBuilder rb{ctx, 2}; 1033 IPC::ResponseBuilder rb{ctx, 2};
1010 rb.Push(result); 1034 rb.Push(result);
1011} 1035}
@@ -1018,7 +1042,7 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) {
1018 1042
1019 auto npad = GetResourceManager()->GetNpad(); 1043 auto npad = GetResourceManager()->GetNpad();
1020 1044
1021 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0); 1045 npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1022 const Result result = npad->Activate(applet_resource_user_id); 1046 const Result result = npad->Activate(applet_resource_user_id);
1023 1047
1024 IPC::ResponseBuilder rb{ctx, 2}; 1048 IPC::ResponseBuilder rb{ctx, 2};
@@ -1052,13 +1076,13 @@ void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) {
1052 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 1076 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
1053 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 1077 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
1054 1078
1055 // Games expect this event to be signaled after calling this function 1079 Kernel::KReadableEvent* style_set_update_event;
1056 GetResourceManager()->GetNpad()->SignalStyleSetChangedEvent(parameters.npad_id); 1080 const auto result = GetResourceManager()->GetNpad()->AcquireNpadStyleSetUpdateEventHandle(
1081 parameters.applet_resource_user_id, &style_set_update_event, parameters.npad_id);
1057 1082
1058 IPC::ResponseBuilder rb{ctx, 2, 1}; 1083 IPC::ResponseBuilder rb{ctx, 2, 1};
1059 rb.Push(ResultSuccess); 1084 rb.Push(result);
1060 rb.PushCopyObjects( 1085 rb.PushCopyObjects(style_set_update_event);
1061 GetResourceManager()->GetNpad()->GetStyleSetChangedEvent(parameters.npad_id));
1062} 1086}
1063 1087
1064void IHidServer::DisconnectNpad(HLERequestContext& ctx) { 1088void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
@@ -1073,7 +1097,7 @@ void IHidServer::DisconnectNpad(HLERequestContext& ctx) {
1073 const auto parameters{rp.PopRaw<Parameters>()}; 1097 const auto parameters{rp.PopRaw<Parameters>()};
1074 1098
1075 auto controller = GetResourceManager()->GetNpad(); 1099 auto controller = GetResourceManager()->GetNpad();
1076 controller->DisconnectNpad(parameters.npad_id); 1100 controller->DisconnectNpad(parameters.applet_resource_user_id, parameters.npad_id);
1077 1101
1078 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1102 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1079 parameters.applet_resource_user_id); 1103 parameters.applet_resource_user_id);
@@ -1113,7 +1137,7 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1113 1137
1114 auto npad = GetResourceManager()->GetNpad(); 1138 auto npad = GetResourceManager()->GetNpad();
1115 1139
1116 // TODO: npad->SetRevision(applet_resource_user_id, revision); 1140 npad->SetRevision(parameters.applet_resource_user_id, parameters.revision);
1117 const auto result = npad->Activate(parameters.applet_resource_user_id); 1141 const auto result = npad->Activate(parameters.applet_resource_user_id);
1118 1142
1119 IPC::ResponseBuilder rb{ctx, 2}; 1143 IPC::ResponseBuilder rb{ctx, 2};
@@ -1125,13 +1149,19 @@ void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
1125 const auto applet_resource_user_id{rp.Pop<u64>()}; 1149 const auto applet_resource_user_id{rp.Pop<u64>()};
1126 const auto hold_type{rp.PopEnum<NpadJoyHoldType>()}; 1150 const auto hold_type{rp.PopEnum<NpadJoyHoldType>()};
1127 1151
1128 GetResourceManager()->GetNpad()->SetHoldType(hold_type);
1129
1130 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", 1152 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
1131 applet_resource_user_id, hold_type); 1153 applet_resource_user_id, hold_type);
1132 1154
1155 if (hold_type != NpadJoyHoldType::Horizontal && hold_type != NpadJoyHoldType::Vertical) {
1156 // This should crash console
1157 ASSERT_MSG(false, "Invalid npad joy hold type");
1158 }
1159
1160 const auto npad = GetResourceManager()->GetNpad();
1161 const auto result = npad->SetNpadJoyHoldType(applet_resource_user_id, hold_type);
1162
1133 IPC::ResponseBuilder rb{ctx, 2}; 1163 IPC::ResponseBuilder rb{ctx, 2};
1134 rb.Push(ResultSuccess); 1164 rb.Push(result);
1135} 1165}
1136 1166
1137void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { 1167void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
@@ -1140,9 +1170,13 @@ void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) {
1140 1170
1141 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1171 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1142 1172
1173 NpadJoyHoldType hold_type{};
1174 const auto npad = GetResourceManager()->GetNpad();
1175 const auto result = npad->GetNpadJoyHoldType(applet_resource_user_id, hold_type);
1176
1143 IPC::ResponseBuilder rb{ctx, 4}; 1177 IPC::ResponseBuilder rb{ctx, 4};
1144 rb.Push(ResultSuccess); 1178 rb.Push(result);
1145 rb.PushEnum(GetResourceManager()->GetNpad()->GetHoldType()); 1179 rb.PushEnum(hold_type);
1146} 1180}
1147 1181
1148void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { 1182void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) {
@@ -1158,8 +1192,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx)
1158 1192
1159 Core::HID::NpadIdType new_npad_id{}; 1193 Core::HID::NpadIdType new_npad_id{};
1160 auto controller = GetResourceManager()->GetNpad(); 1194 auto controller = GetResourceManager()->GetNpad();
1161 controller->SetNpadMode(new_npad_id, parameters.npad_id, NpadJoyDeviceType::Left, 1195 controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
1162 NpadJoyAssignmentMode::Single); 1196 NpadJoyDeviceType::Left, NpadJoyAssignmentMode::Single);
1163 1197
1164 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1198 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1165 parameters.applet_resource_user_id); 1199 parameters.applet_resource_user_id);
@@ -1182,8 +1216,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) {
1182 1216
1183 Core::HID::NpadIdType new_npad_id{}; 1217 Core::HID::NpadIdType new_npad_id{};
1184 auto controller = GetResourceManager()->GetNpad(); 1218 auto controller = GetResourceManager()->GetNpad();
1185 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1219 controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
1186 NpadJoyAssignmentMode::Single); 1220 parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single);
1187 1221
1188 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1222 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1189 parameters.npad_id, parameters.applet_resource_user_id, 1223 parameters.npad_id, parameters.applet_resource_user_id,
@@ -1206,7 +1240,8 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) {
1206 1240
1207 Core::HID::NpadIdType new_npad_id{}; 1241 Core::HID::NpadIdType new_npad_id{};
1208 auto controller = GetResourceManager()->GetNpad(); 1242 auto controller = GetResourceManager()->GetNpad();
1209 controller->SetNpadMode(new_npad_id, parameters.npad_id, {}, NpadJoyAssignmentMode::Dual); 1243 controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id, {},
1244 NpadJoyAssignmentMode::Dual);
1210 1245
1211 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1246 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1212 parameters.applet_resource_user_id); // Spams a lot when controller applet is open 1247 parameters.applet_resource_user_id); // Spams a lot when controller applet is open
@@ -1222,7 +1257,8 @@ void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) {
1222 const auto applet_resource_user_id{rp.Pop<u64>()}; 1257 const auto applet_resource_user_id{rp.Pop<u64>()};
1223 1258
1224 auto controller = GetResourceManager()->GetNpad(); 1259 auto controller = GetResourceManager()->GetNpad();
1225 const auto result = controller->MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); 1260 const auto result =
1261 controller->MergeSingleJoyAsDualJoy(applet_resource_user_id, npad_id_1, npad_id_2);
1226 1262
1227 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1263 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1228 npad_id_1, npad_id_2, applet_resource_user_id); 1264 npad_id_1, npad_id_2, applet_resource_user_id);
@@ -1235,10 +1271,10 @@ void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) {
1235 IPC::RequestParser rp{ctx}; 1271 IPC::RequestParser rp{ctx};
1236 const auto applet_resource_user_id{rp.Pop<u64>()}; 1272 const auto applet_resource_user_id{rp.Pop<u64>()};
1237 1273
1238 GetResourceManager()->GetNpad()->StartLRAssignmentMode();
1239
1240 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1274 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1241 1275
1276 GetResourceManager()->GetNpad()->StartLrAssignmentMode(applet_resource_user_id);
1277
1242 IPC::ResponseBuilder rb{ctx, 2}; 1278 IPC::ResponseBuilder rb{ctx, 2};
1243 rb.Push(ResultSuccess); 1279 rb.Push(ResultSuccess);
1244} 1280}
@@ -1247,10 +1283,10 @@ void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) {
1247 IPC::RequestParser rp{ctx}; 1283 IPC::RequestParser rp{ctx};
1248 const auto applet_resource_user_id{rp.Pop<u64>()}; 1284 const auto applet_resource_user_id{rp.Pop<u64>()};
1249 1285
1250 GetResourceManager()->GetNpad()->StopLRAssignmentMode();
1251
1252 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1286 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1253 1287
1288 GetResourceManager()->GetNpad()->StopLrAssignmentMode(applet_resource_user_id);
1289
1254 IPC::ResponseBuilder rb{ctx, 2}; 1290 IPC::ResponseBuilder rb{ctx, 2};
1255 rb.Push(ResultSuccess); 1291 rb.Push(ResultSuccess);
1256} 1292}
@@ -1260,13 +1296,23 @@ void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) {
1260 const auto applet_resource_user_id{rp.Pop<u64>()}; 1296 const auto applet_resource_user_id{rp.Pop<u64>()};
1261 const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()}; 1297 const auto activation_mode{rp.PopEnum<NpadHandheldActivationMode>()};
1262 1298
1263 GetResourceManager()->GetNpad()->SetNpadHandheldActivationMode(activation_mode);
1264
1265 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", 1299 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
1266 applet_resource_user_id, activation_mode); 1300 applet_resource_user_id, activation_mode);
1267 1301
1302 if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
1303 // Console should crash here
1304 ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
1305 IPC::ResponseBuilder rb{ctx, 2};
1306 rb.Push(ResultSuccess);
1307 return;
1308 }
1309
1310 const auto npad = GetResourceManager()->GetNpad();
1311 const auto result =
1312 npad->SetNpadHandheldActivationMode(applet_resource_user_id, activation_mode);
1313
1268 IPC::ResponseBuilder rb{ctx, 2}; 1314 IPC::ResponseBuilder rb{ctx, 2};
1269 rb.Push(ResultSuccess); 1315 rb.Push(result);
1270} 1316}
1271 1317
1272void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { 1318void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
@@ -1275,9 +1321,14 @@ void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) {
1275 1321
1276 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1322 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1277 1323
1324 NpadHandheldActivationMode activation_mode{};
1325 const auto npad = GetResourceManager()->GetNpad();
1326 const auto result =
1327 npad->GetNpadHandheldActivationMode(applet_resource_user_id, activation_mode);
1328
1278 IPC::ResponseBuilder rb{ctx, 4}; 1329 IPC::ResponseBuilder rb{ctx, 4};
1279 rb.Push(ResultSuccess); 1330 rb.Push(result);
1280 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadHandheldActivationMode()); 1331 rb.PushEnum(activation_mode);
1281} 1332}
1282 1333
1283void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { 1334void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
@@ -1286,12 +1337,12 @@ void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) {
1286 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1337 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1287 const auto applet_resource_user_id{rp.Pop<u64>()}; 1338 const auto applet_resource_user_id{rp.Pop<u64>()};
1288 1339
1289 auto controller = GetResourceManager()->GetNpad();
1290 const auto result = controller->SwapNpadAssignment(npad_id_1, npad_id_2);
1291
1292 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1340 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1293 npad_id_1, npad_id_2, applet_resource_user_id); 1341 npad_id_1, npad_id_2, applet_resource_user_id);
1294 1342
1343 const auto npad = GetResourceManager()->GetNpad();
1344 const auto result = npad->SwapNpadAssignment(applet_resource_user_id, npad_id_1, npad_id_2);
1345
1295 IPC::ResponseBuilder rb{ctx, 2}; 1346 IPC::ResponseBuilder rb{ctx, 2};
1296 rb.Push(result); 1347 rb.Push(result);
1297} 1348}
@@ -1307,13 +1358,19 @@ void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext&
1307 1358
1308 const auto parameters{rp.PopRaw<Parameters>()}; 1359 const auto parameters{rp.PopRaw<Parameters>()};
1309 1360
1310 bool is_enabled = false; 1361 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1311 auto controller = GetResourceManager()->GetNpad(); 1362 parameters.applet_resource_user_id);
1312 const auto result =
1313 controller->IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1314 1363
1315 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1364 if (!IsNpadIdValid(parameters.npad_id)) {
1316 parameters.npad_id, parameters.applet_resource_user_id); 1365 IPC::ResponseBuilder rb{ctx, 3};
1366 rb.Push(ResultInvalidNpadId);
1367 return;
1368 }
1369
1370 bool is_enabled{};
1371 const auto npad = GetResourceManager()->GetNpad();
1372 const auto result = npad->IsUnintendedHomeButtonInputProtectionEnabled(
1373 is_enabled, parameters.applet_resource_user_id, parameters.npad_id);
1317 1374
1318 IPC::ResponseBuilder rb{ctx, 3}; 1375 IPC::ResponseBuilder rb{ctx, 3};
1319 rb.Push(result); 1376 rb.Push(result);
@@ -1332,13 +1389,18 @@ void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ct
1332 1389
1333 const auto parameters{rp.PopRaw<Parameters>()}; 1390 const auto parameters{rp.PopRaw<Parameters>()};
1334 1391
1335 auto controller = GetResourceManager()->GetNpad(); 1392 LOG_INFO(Service_HID, "called, is_enabled={}, npad_id={}, applet_resource_user_id={}",
1336 const auto result = controller->SetUnintendedHomeButtonInputProtectionEnabled( 1393 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id);
1337 parameters.is_enabled, parameters.npad_id);
1338 1394
1339 LOG_DEBUG(Service_HID, 1395 if (!IsNpadIdValid(parameters.npad_id)) {
1340 "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", 1396 IPC::ResponseBuilder rb{ctx, 3};
1341 parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); 1397 rb.Push(ResultInvalidNpadId);
1398 return;
1399 }
1400
1401 const auto npad = GetResourceManager()->GetNpad();
1402 const auto result = npad->EnableUnintendedHomeButtonInputProtection(
1403 parameters.applet_resource_user_id, parameters.npad_id, parameters.is_enabled);
1342 1404
1343 IPC::ResponseBuilder rb{ctx, 2}; 1405 IPC::ResponseBuilder rb{ctx, 2};
1344 rb.Push(result); 1406 rb.Push(result);
@@ -1359,8 +1421,8 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
1359 Core::HID::NpadIdType new_npad_id{}; 1421 Core::HID::NpadIdType new_npad_id{};
1360 auto controller = GetResourceManager()->GetNpad(); 1422 auto controller = GetResourceManager()->GetNpad();
1361 const auto is_reassigned = 1423 const auto is_reassigned =
1362 controller->SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, 1424 controller->SetNpadMode(parameters.applet_resource_user_id, new_npad_id, parameters.npad_id,
1363 NpadJoyAssignmentMode::Single); 1425 parameters.npad_joy_device_type, NpadJoyAssignmentMode::Single);
1364 1426
1365 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1427 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1366 parameters.npad_id, parameters.applet_resource_user_id, 1428 parameters.npad_id, parameters.applet_resource_user_id,
@@ -1375,7 +1437,7 @@ void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext
1375void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { 1437void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1376 IPC::RequestParser rp{ctx}; 1438 IPC::RequestParser rp{ctx};
1377 struct Parameters { 1439 struct Parameters {
1378 bool analog_stick_use_center_clamp; 1440 bool use_center_clamp;
1379 INSERT_PADDING_BYTES_NOINIT(7); 1441 INSERT_PADDING_BYTES_NOINIT(7);
1380 u64 applet_resource_user_id; 1442 u64 applet_resource_user_id;
1381 }; 1443 };
@@ -1383,12 +1445,11 @@ void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) {
1383 1445
1384 const auto parameters{rp.PopRaw<Parameters>()}; 1446 const auto parameters{rp.PopRaw<Parameters>()};
1385 1447
1386 GetResourceManager()->GetNpad()->SetAnalogStickUseCenterClamp( 1448 LOG_WARNING(Service_HID, "(STUBBED) called, use_center_clamp={}, applet_resource_user_id={}",
1387 parameters.analog_stick_use_center_clamp); 1449 parameters.use_center_clamp, parameters.applet_resource_user_id);
1388 1450
1389 LOG_WARNING(Service_HID, 1451 GetResourceManager()->GetNpad()->SetNpadAnalogStickUseCenterClamp(
1390 "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", 1452 parameters.applet_resource_user_id, parameters.use_center_clamp);
1391 parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id);
1392 1453
1393 IPC::ResponseBuilder rb{ctx, 2}; 1454 IPC::ResponseBuilder rb{ctx, 2};
1394 rb.Push(ResultSuccess); 1455 rb.Push(ResultSuccess);
@@ -1496,7 +1557,8 @@ void IHidServer::SendVibrationValue(HLERequestContext& ctx) {
1496 1557
1497 const auto parameters{rp.PopRaw<Parameters>()}; 1558 const auto parameters{rp.PopRaw<Parameters>()};
1498 1559
1499 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, 1560 GetResourceManager()->GetNpad()->VibrateController(parameters.applet_resource_user_id,
1561 parameters.vibration_device_handle,
1500 parameters.vibration_value); 1562 parameters.vibration_value);
1501 1563
1502 LOG_DEBUG(Service_HID, 1564 LOG_DEBUG(Service_HID,
@@ -1528,8 +1590,8 @@ void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) {
1528 1590
1529 IPC::ResponseBuilder rb{ctx, 6}; 1591 IPC::ResponseBuilder rb{ctx, 6};
1530 rb.Push(ResultSuccess); 1592 rb.Push(ResultSuccess);
1531 rb.PushRaw( 1593 rb.PushRaw(GetResourceManager()->GetNpad()->GetLastVibration(
1532 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle)); 1594 parameters.applet_resource_user_id, parameters.vibration_device_handle));
1533} 1595}
1534 1596
1535void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { 1597void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) {
@@ -1580,7 +1642,8 @@ void IHidServer::SendVibrationValues(HLERequestContext& ctx) {
1580 auto vibration_values = std::span( 1642 auto vibration_values = std::span(
1581 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count); 1643 reinterpret_cast<const Core::HID::VibrationValue*>(vibration_data.data()), vibration_count);
1582 1644
1583 GetResourceManager()->GetNpad()->VibrateControllers(vibration_device_handles, vibration_values); 1645 GetResourceManager()->GetNpad()->VibrateControllers(applet_resource_user_id,
1646 vibration_device_handles, vibration_values);
1584 1647
1585 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1648 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1586 1649
@@ -1634,8 +1697,8 @@ void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) {
1634 } 1697 }
1635 }(); 1698 }();
1636 1699
1637 GetResourceManager()->GetNpad()->VibrateController(parameters.vibration_device_handle, 1700 GetResourceManager()->GetNpad()->VibrateController(
1638 vibration_value); 1701 parameters.applet_resource_user_id, parameters.vibration_device_handle, vibration_value);
1639 1702
1640 LOG_DEBUG(Service_HID, 1703 LOG_DEBUG(Service_HID,
1641 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " 1704 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, "
@@ -1659,8 +1722,8 @@ void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) {
1659 1722
1660 const auto parameters{rp.PopRaw<Parameters>()}; 1723 const auto parameters{rp.PopRaw<Parameters>()};
1661 1724
1662 const auto last_vibration = 1725 const auto last_vibration = GetResourceManager()->GetNpad()->GetLastVibration(
1663 GetResourceManager()->GetNpad()->GetLastVibration(parameters.vibration_device_handle); 1726 parameters.applet_resource_user_id, parameters.vibration_device_handle);
1664 1727
1665 const auto gc_erm_command = [last_vibration] { 1728 const auto gc_erm_command = [last_vibration] {
1666 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { 1729 if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) {
@@ -1732,7 +1795,7 @@ void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) {
1732 IPC::ResponseBuilder rb{ctx, 3}; 1795 IPC::ResponseBuilder rb{ctx, 3};
1733 rb.Push(ResultSuccess); 1796 rb.Push(ResultSuccess);
1734 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted( 1797 rb.Push(GetResourceManager()->GetNpad()->IsVibrationDeviceMounted(
1735 parameters.vibration_device_handle)); 1798 parameters.applet_resource_user_id, parameters.vibration_device_handle));
1736} 1799}
1737 1800
1738void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { 1801void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
@@ -1850,8 +1913,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1850 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); 1913 ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes");
1851 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); 1914 ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes");
1852 1915
1853 auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 1916 auto t_mem_1 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_1_handle);
1854 t_mem_1_handle);
1855 1917
1856 if (t_mem_1.IsNull()) { 1918 if (t_mem_1.IsNull()) {
1857 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); 1919 LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle);
@@ -1860,8 +1922,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1860 return; 1922 return;
1861 } 1923 }
1862 1924
1863 auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 1925 auto t_mem_2 = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_2_handle);
1864 t_mem_2_handle);
1865 1926
1866 if (t_mem_2.IsNull()) { 1927 if (t_mem_2.IsNull()) {
1867 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); 1928 LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle);
@@ -2142,8 +2203,7 @@ void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) {
2142 2203
2143 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); 2204 ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
2144 2205
2145 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 2206 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
2146 t_mem_handle);
2147 2207
2148 if (t_mem.IsNull()) { 2208 if (t_mem.IsNull()) {
2149 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 2209 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -2318,10 +2378,10 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2318 const auto applet_resource_user_id{rp.Pop<u64>()}; 2378 const auto applet_resource_user_id{rp.Pop<u64>()};
2319 const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()}; 2379 const auto communication_mode{rp.PopEnum<NpadCommunicationMode>()};
2320 2380
2321 GetResourceManager()->GetNpad()->SetNpadCommunicationMode(communication_mode); 2381 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, communication_mode={}",
2382 applet_resource_user_id, communication_mode);
2322 2383
2323 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", 2384 // This function has been stubbed since 2.0.0+
2324 applet_resource_user_id, communication_mode);
2325 2385
2326 IPC::ResponseBuilder rb{ctx, 2}; 2386 IPC::ResponseBuilder rb{ctx, 2};
2327 rb.Push(ResultSuccess); 2387 rb.Push(ResultSuccess);
@@ -2329,12 +2389,15 @@ void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) {
2329 2389
2330void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { 2390void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) {
2331 IPC::RequestParser rp{ctx}; 2391 IPC::RequestParser rp{ctx};
2392 const auto applet_resource_user_id{rp.Pop<u64>()};
2332 2393
2333 LOG_WARNING(Service_HID, "(STUBBED) called"); 2394 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
2395
2396 // This function has been stubbed since 2.0.0+
2334 2397
2335 IPC::ResponseBuilder rb{ctx, 4}; 2398 IPC::ResponseBuilder rb{ctx, 4};
2336 rb.Push(ResultSuccess); 2399 rb.Push(ResultSuccess);
2337 rb.PushEnum(GetResourceManager()->GetNpad()->GetNpadCommunicationMode()); 2400 rb.PushEnum(NpadCommunicationMode::Default);
2338} 2401}
2339 2402
2340void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { 2403void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp
index 5cc88c4a1..4823de743 100644
--- a/src/core/hle/service/hid/hid_system_server.cpp
+++ b/src/core/hle/service/hid/hid_system_server.cpp
@@ -240,9 +240,12 @@ IHidSystemServer::~IHidSystemServer() {
240}; 240};
241 241
242void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { 242void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) {
243 LOG_WARNING(Service_HID, "called"); 243 IPC::RequestParser rp{ctx};
244 const auto applet_resource_user_id{rp.Pop<u64>()};
245
246 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
244 247
245 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); 248 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(applet_resource_user_id);
246 249
247 IPC::ResponseBuilder rb{ctx, 2}; 250 IPC::ResponseBuilder rb{ctx, 2};
248 rb.Push(ResultSuccess); 251 rb.Push(ResultSuccess);
@@ -271,9 +274,12 @@ void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) {
271} 274}
272 275
273void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) { 276void IHidSystemServer::ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx) {
274 LOG_WARNING(Service_HID, "called"); 277 IPC::RequestParser rp{ctx};
278 const auto applet_resource_user_id{rp.Pop<u64>()};
275 279
276 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicy(); 280 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
281
282 GetResourceManager()->GetNpad()->ApplyNpadSystemCommonPolicyFull(applet_resource_user_id);
277 283
278 IPC::ResponseBuilder rb{ctx, 2}; 284 IPC::ResponseBuilder rb{ctx, 2};
279 rb.Push(ResultSuccess); 285 rb.Push(ResultSuccess);
@@ -298,28 +304,32 @@ void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) {
298 304
299void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { 305void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) {
300 IPC::RequestParser rp{ctx}; 306 IPC::RequestParser rp{ctx};
307 const auto applet_resource_user_id{rp.Pop<u64>()};
301 308
302 LOG_INFO(Service_HID, "(STUBBED) called"); 309 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
303 310
304 Core::HID::NpadStyleSet supported_styleset = 311 Core::HID::NpadStyleSet supported_styleset{};
305 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; 312 const auto& npad = GetResourceManager()->GetNpad();
313 const Result result =
314 npad->GetMaskedSupportedNpadStyleSet(applet_resource_user_id, supported_styleset);
306 315
307 IPC::ResponseBuilder rb{ctx, 3}; 316 IPC::ResponseBuilder rb{ctx, 3};
308 rb.Push(ResultSuccess); 317 rb.Push(result);
309 rb.PushEnum(supported_styleset); 318 rb.PushEnum(supported_styleset);
310} 319}
311 320
312void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { 321void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) {
313 IPC::RequestParser rp{ctx}; 322 IPC::RequestParser rp{ctx};
323 const auto applet_resource_user_id{rp.Pop<u64>()};
314 324
315 LOG_INFO(Service_HID, "(STUBBED) called"); 325 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
316 326
317 Core::HID::NpadStyleSet supported_styleset = 327 const auto& npad = GetResourceManager()->GetNpad();
318 GetResourceManager()->GetNpad()->GetSupportedStyleSet().raw; 328 const auto result =
329 npad->SetSupportedNpadStyleSet(applet_resource_user_id, Core::HID::NpadStyleSet::All);
319 330
320 IPC::ResponseBuilder rb{ctx, 3}; 331 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(ResultSuccess); 332 rb.Push(result);
322 rb.PushEnum(supported_styleset);
323} 333}
324 334
325void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { 335void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid_util.h b/src/core/hle/service/hid/hid_util.h
index b87cc10e3..6a2ed287a 100644
--- a/src/core/hle/service/hid/hid_util.h
+++ b/src/core/hle/service/hid/hid_util.h
@@ -31,7 +31,7 @@ constexpr Result IsSixaxisHandleValid(const Core::HID::SixAxisSensorHandle& hand
31 const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 31 const bool device_index = handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
32 32
33 if (!npad_id) { 33 if (!npad_id) {
34 return InvalidNpadId; 34 return ResultInvalidNpadId;
35 } 35 }
36 if (!device_index) { 36 if (!device_index) {
37 return NpadDeviceIndexOutOfRange; 37 return NpadDeviceIndexOutOfRange;
@@ -54,15 +54,15 @@ constexpr Result IsVibrationHandleValid(const Core::HID::VibrationDeviceHandle&
54 // These support vibration 54 // These support vibration
55 break; 55 break;
56 default: 56 default:
57 return VibrationInvalidStyleIndex; 57 return ResultVibrationInvalidStyleIndex;
58 } 58 }
59 59
60 if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) { 60 if (!IsNpadIdValid(static_cast<Core::HID::NpadIdType>(handle.npad_id))) {
61 return VibrationInvalidNpadId; 61 return ResultVibrationInvalidNpadId;
62 } 62 }
63 63
64 if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) { 64 if (handle.device_index >= Core::HID::DeviceIndex::MaxDeviceIndex) {
65 return VibrationDeviceIndexOutOfRange; 65 return ResultVibrationDeviceIndexOutOfRange;
66 } 66 }
67 67
68 return ResultSuccess; 68 return ResultSuccess;
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index 80aac221b..ffa7e144d 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -49,10 +49,10 @@ HidBus::HidBus(Core::System& system_)
49 // Register update callbacks 49 // Register update callbacks
50 hidbus_update_event = Core::Timing::CreateEvent( 50 hidbus_update_event = Core::Timing::CreateEvent(
51 "Hidbus::UpdateCallback", 51 "Hidbus::UpdateCallback",
52 [this](std::uintptr_t user_data, s64 time, 52 [this](s64 time,
53 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 53 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
54 const auto guard = LockService(); 54 const auto guard = LockService();
55 UpdateHidbus(user_data, ns_late); 55 UpdateHidbus(ns_late);
56 return std::nullopt; 56 return std::nullopt;
57 }); 57 });
58 58
@@ -61,10 +61,10 @@ HidBus::HidBus(Core::System& system_)
61} 61}
62 62
63HidBus::~HidBus() { 63HidBus::~HidBus() {
64 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0); 64 system.CoreTiming().UnscheduleEvent(hidbus_update_event);
65} 65}
66 66
67void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 67void HidBus::UpdateHidbus(std::chrono::nanoseconds ns_late) {
68 if (is_hidbus_enabled) { 68 if (is_hidbus_enabled) {
69 for (std::size_t i = 0; i < devices.size(); ++i) { 69 for (std::size_t i = 0; i < devices.size(); ++i) {
70 if (!devices[i].is_device_initializated) { 70 if (!devices[i].is_device_initializated) {
@@ -448,8 +448,7 @@ void HidBus::EnableJoyPollingReceiveMode(HLERequestContext& ctx) {
448 448
449 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes"); 449 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
450 450
451 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 451 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
452 t_mem_handle);
453 452
454 if (t_mem.IsNull()) { 453 if (t_mem.IsNull()) {
455 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 454 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
index c29b5e882..85a1df133 100644
--- a/src/core/hle/service/hid/hidbus.h
+++ b/src/core/hle/service/hid/hidbus.h
@@ -108,7 +108,7 @@ private:
108 void DisableJoyPollingReceiveMode(HLERequestContext& ctx); 108 void DisableJoyPollingReceiveMode(HLERequestContext& ctx);
109 void SetStatusManagerType(HLERequestContext& ctx); 109 void SetStatusManagerType(HLERequestContext& ctx);
110 110
111 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 111 void UpdateHidbus(std::chrono::nanoseconds ns_late);
112 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const; 112 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
113 113
114 template <typename T> 114 template <typename T>
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 39b9a4474..05ed31273 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -197,8 +197,7 @@ void IRS::RunImageTransferProcessor(HLERequestContext& ctx) {
197 const auto parameters{rp.PopRaw<Parameters>()}; 197 const auto parameters{rp.PopRaw<Parameters>()};
198 const auto t_mem_handle{ctx.GetCopyHandle(0)}; 198 const auto t_mem_handle{ctx.GetCopyHandle(0)};
199 199
200 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 200 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
201 t_mem_handle);
202 201
203 if (t_mem.IsNull()) { 202 if (t_mem.IsNull()) {
204 LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); 203 LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
@@ -316,7 +315,7 @@ void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) {
316 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && 315 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
317 npad_id != Core::HID::NpadIdType::Handheld) { 316 npad_id != Core::HID::NpadIdType::Handheld) {
318 IPC::ResponseBuilder rb{ctx, 2}; 317 IPC::ResponseBuilder rb{ctx, 2};
319 rb.Push(Service::HID::InvalidNpadId); 318 rb.Push(Service::HID::ResultInvalidNpadId);
320 return; 319 return;
321 } 320 }
322 321
@@ -444,8 +443,7 @@ void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) {
444 const auto parameters{rp.PopRaw<Parameters>()}; 443 const auto parameters{rp.PopRaw<Parameters>()};
445 const auto t_mem_handle{ctx.GetCopyHandle(0)}; 444 const auto t_mem_handle{ctx.GetCopyHandle(0)};
446 445
447 auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( 446 auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle);
448 t_mem_handle);
449 447
450 LOG_INFO(Service_IRS, 448 LOG_INFO(Service_IRS,
451 "called, npad_type={}, npad_id={}, transfer_memory_size={}, " 449 "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index 6c6cbd802..1f41e645d 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -10,18 +10,23 @@
10#include "core/hle/service/ipc_helpers.h" 10#include "core/hle/service/ipc_helpers.h"
11 11
12#include "core/hle/service/hid/controllers/applet_resource.h" 12#include "core/hle/service/hid/controllers/applet_resource.h"
13#include "core/hle/service/hid/controllers/capture_button.h"
13#include "core/hle/service/hid/controllers/console_six_axis.h" 14#include "core/hle/service/hid/controllers/console_six_axis.h"
15#include "core/hle/service/hid/controllers/debug_mouse.h"
14#include "core/hle/service/hid/controllers/debug_pad.h" 16#include "core/hle/service/hid/controllers/debug_pad.h"
17#include "core/hle/service/hid/controllers/digitizer.h"
15#include "core/hle/service/hid/controllers/gesture.h" 18#include "core/hle/service/hid/controllers/gesture.h"
19#include "core/hle/service/hid/controllers/home_button.h"
16#include "core/hle/service/hid/controllers/keyboard.h" 20#include "core/hle/service/hid/controllers/keyboard.h"
17#include "core/hle/service/hid/controllers/mouse.h" 21#include "core/hle/service/hid/controllers/mouse.h"
18#include "core/hle/service/hid/controllers/npad.h" 22#include "core/hle/service/hid/controllers/npad.h"
19#include "core/hle/service/hid/controllers/palma.h" 23#include "core/hle/service/hid/controllers/palma.h"
20#include "core/hle/service/hid/controllers/seven_six_axis.h" 24#include "core/hle/service/hid/controllers/seven_six_axis.h"
21#include "core/hle/service/hid/controllers/shared_memory_format.h"
22#include "core/hle/service/hid/controllers/six_axis.h" 25#include "core/hle/service/hid/controllers/six_axis.h"
23#include "core/hle/service/hid/controllers/stubbed.h" 26#include "core/hle/service/hid/controllers/sleep_button.h"
24#include "core/hle/service/hid/controllers/touchscreen.h" 27#include "core/hle/service/hid/controllers/touchscreen.h"
28#include "core/hle/service/hid/controllers/types/shared_memory_format.h"
29#include "core/hle/service/hid/controllers/unique_pad.h"
25 30
26namespace Service::HID { 31namespace Service::HID {
27 32
@@ -46,42 +51,13 @@ void ResourceManager::Initialize() {
46 } 51 }
47 52
48 system.HIDCore().ReloadInputDevices(); 53 system.HIDCore().ReloadInputDevices();
49 is_initialized = true;
50}
51
52void ResourceManager::InitializeController(u64 aruid) {
53 SharedMemoryFormat* shared_memory = nullptr;
54 const auto result = applet_resource->GetSharedMemoryFormat(&shared_memory, aruid);
55 if (result.IsError()) {
56 return;
57 }
58 54
59 debug_pad = std::make_shared<DebugPad>(system.HIDCore(), shared_memory->debug_pad); 55 InitializeHidCommonSampler();
60 mouse = std::make_shared<Mouse>(system.HIDCore(), shared_memory->mouse); 56 InitializeTouchScreenSampler();
61 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore(), shared_memory->debug_mouse); 57 InitializeConsoleSixAxisSampler();
62 keyboard = std::make_shared<Keyboard>(system.HIDCore(), shared_memory->keyboard); 58 InitializeAHidSampler();
63 unique_pad = std::make_shared<UniquePad>(system.HIDCore(), shared_memory->unique_pad.header);
64 npad = std::make_shared<NPad>(system.HIDCore(), shared_memory->npad, service_context);
65 gesture = std::make_shared<Gesture>(system.HIDCore(), shared_memory->gesture);
66 touch_screen = std::make_shared<TouchScreen>(system.HIDCore(), shared_memory->touch_screen);
67 59
68 palma = std::make_shared<Palma>(system.HIDCore(), service_context); 60 is_initialized = true;
69
70 home_button = std::make_shared<HomeButton>(system.HIDCore(), shared_memory->home_button.header);
71 sleep_button =
72 std::make_shared<SleepButton>(system.HIDCore(), shared_memory->sleep_button.header);
73 capture_button =
74 std::make_shared<CaptureButton>(system.HIDCore(), shared_memory->capture_button.header);
75 digitizer = std::make_shared<Digitizer>(system.HIDCore(), shared_memory->digitizer.header);
76
77 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
78 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore(), shared_memory->console);
79 seven_six_axis = std::make_shared<SevenSixAxis>(system);
80
81 // Homebrew doesn't try to activate some controllers, so we activate them by default
82 npad->Activate();
83 six_axis->Activate();
84 touch_screen->Activate();
85} 61}
86 62
87std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const { 63std::shared_ptr<AppletResource> ResourceManager::GetAppletResource() const {
@@ -153,28 +129,77 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
153} 129}
154 130
155Result ResourceManager::CreateAppletResource(u64 aruid) { 131Result ResourceManager::CreateAppletResource(u64 aruid) {
156 if (aruid == 0) { 132 if (aruid == SystemAruid) {
157 const auto result = RegisterCoreAppletResource(); 133 const auto result = RegisterCoreAppletResource();
158 if (result.IsError()) { 134 if (result.IsError()) {
159 return result; 135 return result;
160 } 136 }
161 return GetNpad()->Activate(); 137 return GetNpad()->ActivateNpadResource();
162 } 138 }
163 139
164 const auto result = CreateAppletResourceImpl(aruid); 140 const auto result = CreateAppletResourceImpl(aruid);
165 if (result.IsError()) { 141 if (result.IsError()) {
166 return result; 142 return result;
167 } 143 }
168 return GetNpad()->Activate(aruid); 144
145 // Homebrew doesn't try to activate some controllers, so we activate them by default
146 npad->Activate();
147 six_axis->Activate();
148 touch_screen->Activate();
149
150 return GetNpad()->ActivateNpadResource(aruid);
169} 151}
170 152
171Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { 153Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
172 std::scoped_lock lock{shared_mutex}; 154 std::scoped_lock lock{shared_mutex};
173 const auto result = applet_resource->CreateAppletResource(aruid); 155 return applet_resource->CreateAppletResource(aruid);
174 if (result.IsSuccess()) { 156}
175 InitializeController(aruid); 157
176 } 158void ResourceManager::InitializeHidCommonSampler() {
177 return result; 159 debug_pad = std::make_shared<DebugPad>(system.HIDCore());
160 mouse = std::make_shared<Mouse>(system.HIDCore());
161 debug_mouse = std::make_shared<DebugMouse>(system.HIDCore());
162 keyboard = std::make_shared<Keyboard>(system.HIDCore());
163 unique_pad = std::make_shared<UniquePad>(system.HIDCore());
164 npad = std::make_shared<NPad>(system.HIDCore(), service_context);
165 gesture = std::make_shared<Gesture>(system.HIDCore());
166 home_button = std::make_shared<HomeButton>(system.HIDCore());
167 sleep_button = std::make_shared<SleepButton>(system.HIDCore());
168 capture_button = std::make_shared<CaptureButton>(system.HIDCore());
169 digitizer = std::make_shared<Digitizer>(system.HIDCore());
170
171 palma = std::make_shared<Palma>(system.HIDCore(), service_context);
172 six_axis = std::make_shared<SixAxis>(system.HIDCore(), npad);
173
174 debug_pad->SetAppletResource(applet_resource, &shared_mutex);
175 digitizer->SetAppletResource(applet_resource, &shared_mutex);
176 keyboard->SetAppletResource(applet_resource, &shared_mutex);
177 npad->SetNpadExternals(applet_resource, &shared_mutex);
178 six_axis->SetAppletResource(applet_resource, &shared_mutex);
179 mouse->SetAppletResource(applet_resource, &shared_mutex);
180 debug_mouse->SetAppletResource(applet_resource, &shared_mutex);
181 home_button->SetAppletResource(applet_resource, &shared_mutex);
182 sleep_button->SetAppletResource(applet_resource, &shared_mutex);
183 capture_button->SetAppletResource(applet_resource, &shared_mutex);
184}
185
186void ResourceManager::InitializeTouchScreenSampler() {
187 gesture = std::make_shared<Gesture>(system.HIDCore());
188 touch_screen = std::make_shared<TouchScreen>(system.HIDCore());
189
190 touch_screen->SetAppletResource(applet_resource, &shared_mutex);
191 gesture->SetAppletResource(applet_resource, &shared_mutex);
192}
193
194void ResourceManager::InitializeConsoleSixAxisSampler() {
195 console_six_axis = std::make_shared<ConsoleSixAxis>(system.HIDCore());
196 seven_six_axis = std::make_shared<SevenSixAxis>(system);
197
198 console_six_axis->SetAppletResource(applet_resource, &shared_mutex);
199}
200
201void ResourceManager::InitializeAHidSampler() {
202 // TODO
178} 203}
179 204
180Result ResourceManager::RegisterCoreAppletResource() { 205Result ResourceManager::RegisterCoreAppletResource() {
@@ -189,7 +214,11 @@ Result ResourceManager::UnregisterCoreAppletResource() {
189 214
190Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) { 215Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
191 std::scoped_lock lock{shared_mutex}; 216 std::scoped_lock lock{shared_mutex};
192 return applet_resource->RegisterAppletResourceUserId(aruid, bool_value); 217 auto result = applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
218 if (result.IsSuccess()) {
219 result = npad->RegisterAppletResourceUserId(aruid);
220 }
221 return result;
193} 222}
194 223
195void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) { 224void ResourceManager::UnregisterAppletResourceUserId(u64 aruid) {
@@ -227,8 +256,7 @@ void ResourceManager::EnableTouchScreen(u64 aruid, bool is_enabled) {
227 applet_resource->EnableTouchScreen(aruid, is_enabled); 256 applet_resource->EnableTouchScreen(aruid, is_enabled);
228} 257}
229 258
230void ResourceManager::UpdateControllers(std::uintptr_t user_data, 259void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) {
231 std::chrono::nanoseconds ns_late) {
232 auto& core_timing = system.CoreTiming(); 260 auto& core_timing = system.CoreTiming();
233 debug_pad->OnUpdate(core_timing); 261 debug_pad->OnUpdate(core_timing);
234 digitizer->OnUpdate(core_timing); 262 digitizer->OnUpdate(core_timing);
@@ -241,20 +269,19 @@ void ResourceManager::UpdateControllers(std::uintptr_t user_data,
241 capture_button->OnUpdate(core_timing); 269 capture_button->OnUpdate(core_timing);
242} 270}
243 271
244void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 272void ResourceManager::UpdateNpad(std::chrono::nanoseconds ns_late) {
245 auto& core_timing = system.CoreTiming(); 273 auto& core_timing = system.CoreTiming();
246 npad->OnUpdate(core_timing); 274 npad->OnUpdate(core_timing);
247} 275}
248 276
249void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, 277void ResourceManager::UpdateMouseKeyboard(std::chrono::nanoseconds ns_late) {
250 std::chrono::nanoseconds ns_late) {
251 auto& core_timing = system.CoreTiming(); 278 auto& core_timing = system.CoreTiming();
252 mouse->OnUpdate(core_timing); 279 mouse->OnUpdate(core_timing);
253 debug_mouse->OnUpdate(core_timing); 280 debug_mouse->OnUpdate(core_timing);
254 keyboard->OnUpdate(core_timing); 281 keyboard->OnUpdate(core_timing);
255} 282}
256 283
257void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { 284void ResourceManager::UpdateMotion(std::chrono::nanoseconds ns_late) {
258 auto& core_timing = system.CoreTiming(); 285 auto& core_timing = system.CoreTiming();
259 six_axis->OnUpdate(core_timing); 286 six_axis->OnUpdate(core_timing);
260 seven_six_axis->OnUpdate(core_timing); 287 seven_six_axis->OnUpdate(core_timing);
@@ -273,34 +300,34 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
273 // Register update callbacks 300 // Register update callbacks
274 npad_update_event = Core::Timing::CreateEvent( 301 npad_update_event = Core::Timing::CreateEvent(
275 "HID::UpdatePadCallback", 302 "HID::UpdatePadCallback",
276 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 303 [this, resource](
277 -> std::optional<std::chrono::nanoseconds> { 304 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
278 const auto guard = LockService(); 305 const auto guard = LockService();
279 resource->UpdateNpad(user_data, ns_late); 306 resource->UpdateNpad(ns_late);
280 return std::nullopt; 307 return std::nullopt;
281 }); 308 });
282 default_update_event = Core::Timing::CreateEvent( 309 default_update_event = Core::Timing::CreateEvent(
283 "HID::UpdateDefaultCallback", 310 "HID::UpdateDefaultCallback",
284 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 311 [this, resource](
285 -> std::optional<std::chrono::nanoseconds> { 312 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
286 const auto guard = LockService(); 313 const auto guard = LockService();
287 resource->UpdateControllers(user_data, ns_late); 314 resource->UpdateControllers(ns_late);
288 return std::nullopt; 315 return std::nullopt;
289 }); 316 });
290 mouse_keyboard_update_event = Core::Timing::CreateEvent( 317 mouse_keyboard_update_event = Core::Timing::CreateEvent(
291 "HID::UpdateMouseKeyboardCallback", 318 "HID::UpdateMouseKeyboardCallback",
292 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 319 [this, resource](
293 -> std::optional<std::chrono::nanoseconds> { 320 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
294 const auto guard = LockService(); 321 const auto guard = LockService();
295 resource->UpdateMouseKeyboard(user_data, ns_late); 322 resource->UpdateMouseKeyboard(ns_late);
296 return std::nullopt; 323 return std::nullopt;
297 }); 324 });
298 motion_update_event = Core::Timing::CreateEvent( 325 motion_update_event = Core::Timing::CreateEvent(
299 "HID::UpdateMotionCallback", 326 "HID::UpdateMotionCallback",
300 [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) 327 [this, resource](
301 -> std::optional<std::chrono::nanoseconds> { 328 s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
302 const auto guard = LockService(); 329 const auto guard = LockService();
303 resource->UpdateMotion(user_data, ns_late); 330 resource->UpdateMotion(ns_late);
304 return std::nullopt; 331 return std::nullopt;
305 }); 332 });
306 333
@@ -314,10 +341,10 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource
314} 341}
315 342
316IAppletResource::~IAppletResource() { 343IAppletResource::~IAppletResource() {
317 system.CoreTiming().UnscheduleEvent(npad_update_event, 0); 344 system.CoreTiming().UnscheduleEvent(npad_update_event);
318 system.CoreTiming().UnscheduleEvent(default_update_event, 0); 345 system.CoreTiming().UnscheduleEvent(default_update_event);
319 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); 346 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event);
320 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 347 system.CoreTiming().UnscheduleEvent(motion_update_event);
321 resource_manager->FreeAppletResourceId(aruid); 348 resource_manager->FreeAppletResourceId(aruid);
322} 349}
323 350
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 5ad7cb564..7a21d8eb8 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -20,24 +20,23 @@ class KSharedMemory;
20 20
21namespace Service::HID { 21namespace Service::HID {
22class AppletResource; 22class AppletResource;
23class CaptureButton;
23class Controller_Stubbed; 24class Controller_Stubbed;
24class ConsoleSixAxis; 25class ConsoleSixAxis;
26class DebugMouse;
25class DebugPad; 27class DebugPad;
28class Digitizer;
26class Gesture; 29class Gesture;
30class HomeButton;
27class Keyboard; 31class Keyboard;
28class Mouse; 32class Mouse;
29class NPad; 33class NPad;
30class Palma; 34class Palma;
31class SevenSixAxis; 35class SevenSixAxis;
32class SixAxis; 36class SixAxis;
37class SleepButton;
33class TouchScreen; 38class TouchScreen;
34 39class UniquePad;
35using CaptureButton = Controller_Stubbed;
36using DebugMouse = Mouse;
37using Digitizer = Controller_Stubbed;
38using HomeButton = Controller_Stubbed;
39using SleepButton = Controller_Stubbed;
40using UniquePad = Controller_Stubbed;
41 40
42class ResourceManager { 41class ResourceManager {
43 42
@@ -46,7 +45,6 @@ public:
46 ~ResourceManager(); 45 ~ResourceManager();
47 46
48 void Initialize(); 47 void Initialize();
49 void InitializeController(u64 aruid);
50 48
51 std::shared_ptr<AppletResource> GetAppletResource() const; 49 std::shared_ptr<AppletResource> GetAppletResource() const;
52 std::shared_ptr<CaptureButton> GetCaptureButton() const; 50 std::shared_ptr<CaptureButton> GetCaptureButton() const;
@@ -81,17 +79,21 @@ public:
81 void EnablePadInput(u64 aruid, bool is_enabled); 79 void EnablePadInput(u64 aruid, bool is_enabled);
82 void EnableTouchScreen(u64 aruid, bool is_enabled); 80 void EnableTouchScreen(u64 aruid, bool is_enabled);
83 81
84 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 82 void UpdateControllers(std::chrono::nanoseconds ns_late);
85 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 83 void UpdateNpad(std::chrono::nanoseconds ns_late);
86 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 84 void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late);
87 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 85 void UpdateMotion(std::chrono::nanoseconds ns_late);
88 86
89private: 87private:
90 Result CreateAppletResourceImpl(u64 aruid); 88 Result CreateAppletResourceImpl(u64 aruid);
89 void InitializeHidCommonSampler();
90 void InitializeTouchScreenSampler();
91 void InitializeConsoleSixAxisSampler();
92 void InitializeAHidSampler();
91 93
92 bool is_initialized{false}; 94 bool is_initialized{false};
93 95
94 mutable std::mutex shared_mutex; 96 mutable std::recursive_mutex shared_mutex;
95 std::shared_ptr<AppletResource> applet_resource = nullptr; 97 std::shared_ptr<AppletResource> applet_resource = nullptr;
96 98
97 std::shared_ptr<CaptureButton> capture_button = nullptr; 99 std::shared_ptr<CaptureButton> capture_button = nullptr;
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index 38955932c..39df77e43 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -146,10 +146,7 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
146 146
147HLERequestContext::~HLERequestContext() = default; 147HLERequestContext::~HLERequestContext() = default;
148 148
149void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, 149void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
150 bool incoming) {
151 client_handle_table = &process.GetHandleTable();
152
153 IPC::RequestParser rp(src_cmdbuf); 150 IPC::RequestParser rp(src_cmdbuf);
154 command_header = rp.PopRaw<IPC::CommandHeader>(); 151 command_header = rp.PopRaw<IPC::CommandHeader>();
155 152
@@ -162,7 +159,7 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
162 if (command_header->enable_handle_descriptor) { 159 if (command_header->enable_handle_descriptor) {
163 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>(); 160 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
164 if (handle_descriptor_header->send_current_pid) { 161 if (handle_descriptor_header->send_current_pid) {
165 pid = process.GetProcessId(); 162 pid = thread->GetOwnerProcess()->GetProcessId();
166 rp.Skip(2, false); 163 rp.Skip(2, false);
167 } 164 }
168 if (incoming) { 165 if (incoming) {
@@ -270,9 +267,10 @@ void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* sr
270 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 267 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
271} 268}
272 269
273Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, 270Result HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf) {
274 u32_le* src_cmdbuf) { 271 client_handle_table = &thread->GetOwnerProcess()->GetHandleTable();
275 ParseCommandBuffer(process, src_cmdbuf, true); 272
273 ParseCommandBuffer(src_cmdbuf, true);
276 274
277 if (command_header->IsCloseCommand()) { 275 if (command_header->IsCloseCommand()) {
278 // Close does not populate the rest of the IPC header 276 // Close does not populate the rest of the IPC header
@@ -284,9 +282,9 @@ Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& pr
284 return ResultSuccess; 282 return ResultSuccess;
285} 283}
286 284
287Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread) { 285Result HLERequestContext::WriteToOutgoingCommandBuffer() {
288 auto current_offset = handles_offset; 286 auto current_offset = handles_offset;
289 auto& owner_process = *requesting_thread.GetOwnerProcess(); 287 auto& owner_process = *thread->GetOwnerProcess();
290 auto& handle_table = owner_process.GetHandleTable(); 288 auto& handle_table = owner_process.GetHandleTable();
291 289
292 for (auto& object : outgoing_copy_objects) { 290 for (auto& object : outgoing_copy_objects) {
@@ -319,7 +317,7 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(Kernel::KThread& requesti
319 } 317 }
320 318
321 // Copy the translated command buffer back into the thread's command buffer area. 319 // Copy the translated command buffer back into the thread's command buffer area.
322 memory.WriteBlock(requesting_thread.GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32)); 320 memory.WriteBlock(thread->GetTlsAddress(), cmd_buf.data(), write_size * sizeof(u32));
323 321
324 return ResultSuccess; 322 return ResultSuccess;
325} 323}
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index 18d464c63..40d86943e 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -17,6 +17,7 @@
17#include "common/concepts.h" 17#include "common/concepts.h"
18#include "common/swap.h" 18#include "common/swap.h"
19#include "core/hle/ipc.h" 19#include "core/hle/ipc.h"
20#include "core/hle/kernel/k_handle_table.h"
20#include "core/hle/kernel/svc_common.h" 21#include "core/hle/kernel/svc_common.h"
21 22
22union Result; 23union Result;
@@ -196,10 +197,10 @@ public:
196 } 197 }
197 198
198 /// Populates this context with data from the requesting process/thread. 199 /// Populates this context with data from the requesting process/thread.
199 Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf); 200 Result PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf);
200 201
201 /// Writes data from this context back to the requesting process/thread. 202 /// Writes data from this context back to the requesting process/thread.
202 Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread); 203 Result WriteToOutgoingCommandBuffer();
203 204
204 [[nodiscard]] u32_le GetHipcCommand() const { 205 [[nodiscard]] u32_le GetHipcCommand() const {
205 return command; 206 return command;
@@ -359,8 +360,17 @@ public:
359 return *thread; 360 return *thread;
360 } 361 }
361 362
362 Kernel::KHandleTable& GetClientHandleTable() { 363 [[nodiscard]] Core::Memory::Memory& GetMemory() const {
363 return *client_handle_table; 364 return memory;
365 }
366
367 template <typename T>
368 Kernel::KScopedAutoObject<T> GetObjectFromHandle(u32 handle) {
369 auto obj = client_handle_table->GetObjectForIpc(handle, thread);
370 if (obj.IsNotNull()) {
371 return obj->DynamicCast<T*>();
372 }
373 return nullptr;
364 } 374 }
365 375
366 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const { 376 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
@@ -378,7 +388,7 @@ public:
378private: 388private:
379 friend class IPC::ResponseBuilder; 389 friend class IPC::ResponseBuilder;
380 390
381 void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming); 391 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
382 392
383 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 393 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
384 Kernel::KServerSession* server_session{}; 394 Kernel::KServerSession* server_session{};
diff --git a/src/core/hle/service/ipc_helpers.h b/src/core/hle/service/ipc_helpers.h
index 0e222362e..4b02872fb 100644
--- a/src/core/hle/service/ipc_helpers.h
+++ b/src/core/hle/service/ipc_helpers.h
@@ -151,8 +151,8 @@ public:
151 if (manager->IsDomain()) { 151 if (manager->IsDomain()) {
152 context->AddDomainObject(std::move(iface)); 152 context->AddDomainObject(std::move(iface));
153 } else { 153 } else {
154 kernel.ApplicationProcess()->GetResourceLimit()->Reserve( 154 ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(
155 Kernel::LimitableResource::SessionCountMax, 1); 155 Kernel::LimitableResource::SessionCountMax, 1));
156 156
157 auto* session = Kernel::KSession::Create(kernel); 157 auto* session = Kernel::KSession::Create(kernel);
158 session->Initialize(nullptr, 0); 158 session->Initialize(nullptr, 0);
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index 65851fc05..77aa6d7d1 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -4,11 +4,11 @@
4#include "core/arm/debug.h" 4#include "core/arm/debug.h"
5#include "core/arm/symbols.h" 5#include "core/arm/symbols.h"
6#include "core/core.h" 6#include "core/core.h"
7#include "core/hle/kernel/k_code_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h" 7#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/result.h" 8#include "core/hle/result.h"
10#include "core/hle/service/ipc_helpers.h" 9#include "core/hle/service/ipc_helpers.h"
11#include "core/hle/service/jit/jit.h" 10#include "core/hle/service/jit/jit.h"
11#include "core/hle/service/jit/jit_code_memory.h"
12#include "core/hle/service/jit/jit_context.h" 12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/server_manager.h" 13#include "core/hle/service/server_manager.h"
14#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
@@ -23,9 +23,11 @@ struct CodeRange {
23 23
24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { 24class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
25public: 25public:
26 explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx, 26 explicit IJitEnvironment(Core::System& system_,
27 CodeRange user_ro) 27 Kernel::KScopedAutoObject<Kernel::KProcess>&& process_,
28 : ServiceFramework{system_, "IJitEnvironment"}, process{&process_}, 28 CodeMemory&& user_rx_, CodeMemory&& user_ro_)
29 : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)},
30 user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)},
29 context{system_.ApplicationMemory()} { 31 context{system_.ApplicationMemory()} {
30 // clang-format off 32 // clang-format off
31 static const FunctionInfo functions[] = { 33 static const FunctionInfo functions[] = {
@@ -39,10 +41,13 @@ public:
39 RegisterHandlers(functions); 41 RegisterHandlers(functions);
40 42
41 // Identity map user code range into sysmodule context 43 // Identity map user code range into sysmodule context
42 configuration.user_ro_memory = user_ro; 44 configuration.user_rx_memory.size = user_rx.GetSize();
43 configuration.user_rx_memory = user_rx; 45 configuration.user_rx_memory.offset = user_rx.GetAddress();
44 configuration.sys_ro_memory = user_ro; 46 configuration.user_ro_memory.size = user_ro.GetSize();
45 configuration.sys_rx_memory = user_rx; 47 configuration.user_ro_memory.offset = user_ro.GetAddress();
48
49 configuration.sys_rx_memory = configuration.user_rx_memory;
50 configuration.sys_ro_memory = configuration.user_ro_memory;
46 } 51 }
47 52
48 void GenerateCode(HLERequestContext& ctx) { 53 void GenerateCode(HLERequestContext& ctx) {
@@ -188,7 +193,7 @@ public:
188 return; 193 return;
189 } 194 }
190 195
191 auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)}; 196 auto tmem{ctx.GetObjectFromHandle<Kernel::KTransferMemory>(tmem_handle)};
192 if (tmem.IsNull()) { 197 if (tmem.IsNull()) {
193 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); 198 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
194 IPC::ResponseBuilder rb{ctx, 2}; 199 IPC::ResponseBuilder rb{ctx, 2};
@@ -318,6 +323,8 @@ private:
318 } 323 }
319 324
320 Kernel::KScopedAutoObject<Kernel::KProcess> process; 325 Kernel::KScopedAutoObject<Kernel::KProcess> process;
326 CodeMemory user_rx;
327 CodeMemory user_ro;
321 GuestCallbacks callbacks; 328 GuestCallbacks callbacks;
322 JITConfiguration configuration; 329 JITConfiguration configuration;
323 JITContext context; 330 JITContext context;
@@ -335,6 +342,7 @@ public:
335 RegisterHandlers(functions); 342 RegisterHandlers(functions);
336 } 343 }
337 344
345private:
338 void CreateJitEnvironment(HLERequestContext& ctx) { 346 void CreateJitEnvironment(HLERequestContext& ctx) {
339 LOG_DEBUG(Service_JIT, "called"); 347 LOG_DEBUG(Service_JIT, "called");
340 348
@@ -356,11 +364,7 @@ public:
356 return; 364 return;
357 } 365 }
358 366
359 // Fetch using the handle table for the application process here, 367 auto process{ctx.GetObjectFromHandle<Kernel::KProcess>(process_handle)};
360 // since we are not multiprocess yet.
361 const auto& handle_table{system.ApplicationProcess()->GetHandleTable()};
362
363 auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
364 if (process.IsNull()) { 368 if (process.IsNull()) {
365 LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); 369 LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
366 IPC::ResponseBuilder rb{ctx, 2}; 370 IPC::ResponseBuilder rb{ctx, 2};
@@ -368,7 +372,7 @@ public:
368 return; 372 return;
369 } 373 }
370 374
371 auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)}; 375 auto rx_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(rx_mem_handle)};
372 if (rx_mem.IsNull()) { 376 if (rx_mem.IsNull()) {
373 LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); 377 LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
374 IPC::ResponseBuilder rb{ctx, 2}; 378 IPC::ResponseBuilder rb{ctx, 2};
@@ -376,7 +380,7 @@ public:
376 return; 380 return;
377 } 381 }
378 382
379 auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)}; 383 auto ro_mem{ctx.GetObjectFromHandle<Kernel::KCodeMemory>(ro_mem_handle)};
380 if (ro_mem.IsNull()) { 384 if (ro_mem.IsNull()) {
381 LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); 385 LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
382 IPC::ResponseBuilder rb{ctx, 2}; 386 IPC::ResponseBuilder rb{ctx, 2};
@@ -384,20 +388,35 @@ public:
384 return; 388 return;
385 } 389 }
386 390
387 const CodeRange user_rx{ 391 CodeMemory rx, ro;
388 .offset = GetInteger(rx_mem->GetSourceAddress()), 392 Result res;
389 .size = parameters.rx_size,
390 };
391 393
392 const CodeRange user_ro{ 394 res = rx.Initialize(*process, *rx_mem, parameters.rx_size,
393 .offset = GetInteger(ro_mem->GetSourceAddress()), 395 Kernel::Svc::MemoryPermission::ReadExecute, generate_random);
394 .size = parameters.ro_size, 396 if (R_FAILED(res)) {
395 }; 397 LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle);
398 IPC::ResponseBuilder rb{ctx, 2};
399 rb.Push(res);
400 return;
401 }
402
403 res = ro.Initialize(*process, *ro_mem, parameters.ro_size,
404 Kernel::Svc::MemoryPermission::Read, generate_random);
405 if (R_FAILED(res)) {
406 LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle);
407 IPC::ResponseBuilder rb{ctx, 2};
408 rb.Push(res);
409 return;
410 }
396 411
397 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 412 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
398 rb.Push(ResultSuccess); 413 rb.Push(ResultSuccess);
399 rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro); 414 rb.PushIpcInterface<IJitEnvironment>(system, std::move(process), std::move(rx),
415 std::move(ro));
400 } 416 }
417
418private:
419 std::mt19937_64 generate_random{};
401}; 420};
402 421
403void LoopProcess(Core::System& system) { 422void LoopProcess(Core::System& system) {
diff --git a/src/core/hle/service/jit/jit_code_memory.cpp b/src/core/hle/service/jit/jit_code_memory.cpp
new file mode 100644
index 000000000..2b480488a
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.cpp
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/jit/jit_code_memory.h"
5
6namespace Service::JIT {
7
8Result CodeMemory::Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory,
9 size_t size, Kernel::Svc::MemoryPermission perm,
10 std::mt19937_64& generate_random) {
11 auto& page_table = process.GetPageTable();
12 const u64 alias_code_start =
13 GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize;
14 const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize;
15
16 // NOTE: This will retry indefinitely until mapping the code memory succeeds.
17 while (true) {
18 // Generate a new trial address.
19 const u64 mapped_address =
20 (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize;
21
22 // Try to map the address
23 R_TRY_CATCH(code_memory.MapToOwner(mapped_address, size, perm)) {
24 R_CATCH(Kernel::ResultInvalidMemoryRegion) {
25 // If we could not map here, retry.
26 continue;
27 }
28 }
29 R_END_TRY_CATCH;
30
31 // Set members.
32 m_code_memory = std::addressof(code_memory);
33 m_size = size;
34 m_address = mapped_address;
35 m_perm = perm;
36
37 // Open a new reference to the code memory.
38 m_code_memory->Open();
39
40 // We succeeded.
41 R_SUCCEED();
42 }
43}
44
45void CodeMemory::Finalize() {
46 if (m_code_memory) {
47 R_ASSERT(m_code_memory->UnmapFromOwner(m_address, m_size));
48 m_code_memory->Close();
49 }
50
51 m_code_memory = nullptr;
52}
53
54} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_code_memory.h b/src/core/hle/service/jit/jit_code_memory.h
new file mode 100644
index 000000000..6376d4c4e
--- /dev/null
+++ b/src/core/hle/service/jit/jit_code_memory.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <random>
7
8#include "core/hle/kernel/k_code_memory.h"
9
10namespace Service::JIT {
11
12class CodeMemory {
13public:
14 YUZU_NON_COPYABLE(CodeMemory);
15
16 explicit CodeMemory() = default;
17
18 CodeMemory(CodeMemory&& rhs) {
19 std::swap(m_code_memory, rhs.m_code_memory);
20 std::swap(m_size, rhs.m_size);
21 std::swap(m_address, rhs.m_address);
22 std::swap(m_perm, rhs.m_perm);
23 }
24
25 ~CodeMemory() {
26 this->Finalize();
27 }
28
29public:
30 Result Initialize(Kernel::KProcess& process, Kernel::KCodeMemory& code_memory, size_t size,
31 Kernel::Svc::MemoryPermission perm, std::mt19937_64& generate_random);
32 void Finalize();
33
34 size_t GetSize() const {
35 return m_size;
36 }
37
38 u64 GetAddress() const {
39 return m_address;
40 }
41
42private:
43 Kernel::KCodeMemory* m_code_memory{};
44 size_t m_size{};
45 u64 m_address{};
46 Kernel::Svc::MemoryPermission m_perm{};
47};
48
49} // namespace Service::JIT
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 6352b09a9..aa8aaa2d9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -67,7 +67,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
67 // Schedule the screen composition events 67 // Schedule the screen composition events
68 multi_composition_event = Core::Timing::CreateEvent( 68 multi_composition_event = Core::Timing::CreateEvent(
69 "ScreenComposition", 69 "ScreenComposition",
70 [this](std::uintptr_t, s64 time, 70 [this](s64 time,
71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 71 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
72 vsync_signal.Set(); 72 vsync_signal.Set();
73 return std::chrono::nanoseconds(GetNextTicks()); 73 return std::chrono::nanoseconds(GetNextTicks());
@@ -75,7 +75,7 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
75 75
76 single_composition_event = Core::Timing::CreateEvent( 76 single_composition_event = Core::Timing::CreateEvent(
77 "ScreenComposition", 77 "ScreenComposition",
78 [this](std::uintptr_t, s64 time, 78 [this](s64 time,
79 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 79 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
80 const auto lock_guard = Lock(); 80 const auto lock_guard = Lock();
81 Compose(); 81 Compose();
@@ -93,11 +93,11 @@ Nvnflinger::Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_
93 93
94Nvnflinger::~Nvnflinger() { 94Nvnflinger::~Nvnflinger() {
95 if (system.IsMulticore()) { 95 if (system.IsMulticore()) {
96 system.CoreTiming().UnscheduleEvent(multi_composition_event, {}); 96 system.CoreTiming().UnscheduleEvent(multi_composition_event);
97 vsync_thread.request_stop(); 97 vsync_thread.request_stop();
98 vsync_signal.Set(); 98 vsync_signal.Set();
99 } else { 99 } else {
100 system.CoreTiming().UnscheduleEvent(single_composition_event, {}); 100 system.CoreTiming().UnscheduleEvent(single_composition_event);
101 } 101 }
102 102
103 ShutdownLayers(); 103 ShutdownLayers();
diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp
index 17110d3f1..f0658bb5d 100644
--- a/src/core/hle/service/ro/ro.cpp
+++ b/src/core/hle/service/ro/ro.cpp
@@ -651,10 +651,9 @@ private:
651 void RegisterProcessHandle(HLERequestContext& ctx) { 651 void RegisterProcessHandle(HLERequestContext& ctx) {
652 LOG_DEBUG(Service_LDR, "(called)"); 652 LOG_DEBUG(Service_LDR, "(called)");
653 653
654 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); 654 auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
655 auto client_pid = ctx.GetPID(); 655 auto client_pid = ctx.GetPID();
656 auto result = interface.RegisterProcessHandle(client_pid, 656 auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe());
657 process_h->DynamicCast<Kernel::KProcess*>());
658 657
659 IPC::ResponseBuilder rb{ctx, 2}; 658 IPC::ResponseBuilder rb{ctx, 2};
660 rb.Push(result); 659 rb.Push(result);
@@ -671,12 +670,11 @@ private:
671 670
672 IPC::RequestParser rp{ctx}; 671 IPC::RequestParser rp{ctx};
673 auto params = rp.PopRaw<InputParameters>(); 672 auto params = rp.PopRaw<InputParameters>();
674 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0)); 673 auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(ctx.GetCopyHandle(0));
675 674
676 auto client_pid = ctx.GetPID(); 675 auto client_pid = ctx.GetPID();
677 auto result = 676 auto result = interface.RegisterProcessModuleInfo(
678 interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size, 677 client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe());
679 process_h->DynamicCast<Kernel::KProcess*>());
680 678
681 IPC::ResponseBuilder rb{ctx, 2}; 679 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(result); 680 rb.Push(result);
diff --git a/src/core/hle/service/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index 6808247a9..15edb23e0 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -47,7 +47,7 @@ ServerManager::~ServerManager() {
47 m_stopped.Wait(); 47 m_stopped.Wait();
48 m_threads.clear(); 48 m_threads.clear();
49 49
50 // Clean up ports. 50 // Clean up server ports.
51 for (const auto& [port, handler] : m_ports) { 51 for (const auto& [port, handler] : m_ports) {
52 port->Close(); 52 port->Close();
53 } 53 }
@@ -97,22 +97,15 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
97 u32 max_sessions) { 97 u32 max_sessions) {
98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); 98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
99 99
100 // Add the new server to sm:. 100 // Add the new server to sm: and get the moved server port.
101 ASSERT(R_SUCCEEDED( 101 Kernel::KServerPort* server_port{};
102 m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory))); 102 R_ASSERT(m_system.ServiceManager().RegisterService(std::addressof(server_port), service_name,
103 103 max_sessions, handler_factory));
104 // Get the registered port.
105 Kernel::KPort* port{};
106 ASSERT(
107 R_SUCCEEDED(m_system.ServiceManager().GetServicePort(std::addressof(port), service_name)));
108
109 // Open a new reference to the server port.
110 port->GetServerPort().Open();
111 104
112 // Begin tracking the server port. 105 // Begin tracking the server port.
113 { 106 {
114 std::scoped_lock ll{m_list_mutex}; 107 std::scoped_lock ll{m_list_mutex};
115 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory)); 108 m_ports.emplace(server_port, std::move(handler_factory));
116 } 109 }
117 110
118 // Signal the wakeup event. 111 // Signal the wakeup event.
@@ -372,7 +365,7 @@ Result ServerManager::OnSessionEvent(Kernel::KServerSession* session,
372 365
373 // Try to receive a message. 366 // Try to receive a message.
374 std::shared_ptr<HLERequestContext> context; 367 std::shared_ptr<HLERequestContext> context;
375 rc = session->ReceiveRequest(&context, manager); 368 rc = session->ReceiveRequestHLE(&context, manager);
376 369
377 // If the session has been closed, we're done. 370 // If the session has been closed, we're done.
378 if (rc == Kernel::ResultSessionClosed) { 371 if (rc == Kernel::ResultSessionClosed) {
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 00531b021..39124c5fd 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -203,7 +203,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
203 // If emulation was shutdown, we are closing service threads, do not write the response back to 203 // If emulation was shutdown, we are closing service threads, do not write the response back to
204 // memory that may be shutting down as well. 204 // memory that may be shutting down as well.
205 if (system.IsPoweredOn()) { 205 if (system.IsPoweredOn()) {
206 ctx.WriteToOutgoingCommandBuffer(ctx.GetThread()); 206 ctx.WriteToOutgoingCommandBuffer();
207 } 207 }
208 208
209 return result; 209 return result;
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 0653779d5..8e637f963 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -507,6 +507,14 @@ void SET_SYS::SetTvSettings(HLERequestContext& ctx) {
507 rb.Push(ResultSuccess); 507 rb.Push(ResultSuccess);
508} 508}
509 509
510void SET_SYS::GetDebugModeFlag(HLERequestContext& ctx) {
511 LOG_DEBUG(Service_SET, "called");
512
513 IPC::ResponseBuilder rb{ctx, 3};
514 rb.Push(ResultSuccess);
515 rb.Push<u32>(0);
516}
517
510void SET_SYS::GetQuestFlag(HLERequestContext& ctx) { 518void SET_SYS::GetQuestFlag(HLERequestContext& ctx) {
511 LOG_WARNING(Service_SET, "(STUBBED) called"); 519 LOG_WARNING(Service_SET, "(STUBBED) called");
512 520
@@ -926,7 +934,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"},
926 {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"}, 934 {59, &SET_SYS::SetNetworkSystemClockContext, "SetNetworkSystemClockContext"},
927 {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"}, 935 {60, &SET_SYS::IsUserSystemClockAutomaticCorrectionEnabled, "IsUserSystemClockAutomaticCorrectionEnabled"},
928 {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"}, 936 {61, &SET_SYS::SetUserSystemClockAutomaticCorrectionEnabled, "SetUserSystemClockAutomaticCorrectionEnabled"},
929 {62, nullptr, "GetDebugModeFlag"}, 937 {62, &SET_SYS::GetDebugModeFlag, "GetDebugModeFlag"},
930 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"}, 938 {63, &SET_SYS::GetPrimaryAlbumStorage, "GetPrimaryAlbumStorage"},
931 {64, nullptr, "SetPrimaryAlbumStorage"}, 939 {64, nullptr, "SetPrimaryAlbumStorage"},
932 {65, nullptr, "GetUsb30EnableFlag"}, 940 {65, nullptr, "GetUsb30EnableFlag"},
@@ -1143,6 +1151,8 @@ void SET_SYS::StoreSettings() {
1143} 1151}
1144 1152
1145void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) { 1153void SET_SYS::StoreSettingsThreadFunc(std::stop_token stop_token) {
1154 Common::SetCurrentThreadName("SettingsStore");
1155
1146 while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) { 1156 while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
1147 std::scoped_lock l{m_save_needed_mutex}; 1157 std::scoped_lock l{m_save_needed_mutex};
1148 if (!std::exchange(m_save_needed, false)) { 1158 if (!std::exchange(m_save_needed, false)) {
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index 3785d93d8..853f76fce 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -98,6 +98,7 @@ private:
98 void GetSettingsItemValue(HLERequestContext& ctx); 98 void GetSettingsItemValue(HLERequestContext& ctx);
99 void GetTvSettings(HLERequestContext& ctx); 99 void GetTvSettings(HLERequestContext& ctx);
100 void SetTvSettings(HLERequestContext& ctx); 100 void SetTvSettings(HLERequestContext& ctx);
101 void GetDebugModeFlag(HLERequestContext& ctx);
101 void GetQuestFlag(HLERequestContext& ctx); 102 void GetQuestFlag(HLERequestContext& ctx);
102 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx); 103 void GetDeviceTimeZoneLocationName(HLERequestContext& ctx);
103 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx); 104 void SetDeviceTimeZoneLocationName(HLERequestContext& ctx);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 296ee6e89..1095dcf6c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -29,8 +29,7 @@ ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {
29 29
30ServiceManager::~ServiceManager() { 30ServiceManager::~ServiceManager() {
31 for (auto& [name, port] : service_ports) { 31 for (auto& [name, port] : service_ports) {
32 port->GetClientPort().Close(); 32 port->Close();
33 port->GetServerPort().Close();
34 } 33 }
35 34
36 if (deferral_event) { 35 if (deferral_event) {
@@ -50,8 +49,8 @@ static Result ValidateServiceName(const std::string& name) {
50 return ResultSuccess; 49 return ResultSuccess;
51} 50}
52 51
53Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 52Result ServiceManager::RegisterService(Kernel::KServerPort** out_server_port, std::string name,
54 SessionRequestHandlerFactory handler) { 53 u32 max_sessions, SessionRequestHandlerFactory handler) {
55 R_TRY(ValidateServiceName(name)); 54 R_TRY(ValidateServiceName(name));
56 55
57 std::scoped_lock lk{lock}; 56 std::scoped_lock lk{lock};
@@ -66,13 +65,17 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
66 // Register the port. 65 // Register the port.
67 Kernel::KPort::Register(kernel, port); 66 Kernel::KPort::Register(kernel, port);
68 67
69 service_ports.emplace(name, port); 68 service_ports.emplace(name, std::addressof(port->GetClientPort()));
70 registered_services.emplace(name, handler); 69 registered_services.emplace(name, handler);
71 if (deferral_event) { 70 if (deferral_event) {
72 deferral_event->Signal(); 71 deferral_event->Signal();
73 } 72 }
74 73
75 return ResultSuccess; 74 // Set our output.
75 *out_server_port = std::addressof(port->GetServerPort());
76
77 // We succeeded.
78 R_SUCCEED();
76} 79}
77 80
78Result ServiceManager::UnregisterService(const std::string& name) { 81Result ServiceManager::UnregisterService(const std::string& name) {
@@ -91,7 +94,8 @@ Result ServiceManager::UnregisterService(const std::string& name) {
91 return ResultSuccess; 94 return ResultSuccess;
92} 95}
93 96
94Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::string& name) { 97Result ServiceManager::GetServicePort(Kernel::KClientPort** out_client_port,
98 const std::string& name) {
95 R_TRY(ValidateServiceName(name)); 99 R_TRY(ValidateServiceName(name));
96 100
97 std::scoped_lock lk{lock}; 101 std::scoped_lock lk{lock};
@@ -101,7 +105,7 @@ Result ServiceManager::GetServicePort(Kernel::KPort** out_port, const std::strin
101 return Service::SM::ResultNotRegistered; 105 return Service::SM::ResultNotRegistered;
102 } 106 }
103 107
104 *out_port = it->second; 108 *out_client_port = it->second;
105 return ResultSuccess; 109 return ResultSuccess;
106} 110}
107 111
@@ -172,8 +176,8 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
172 std::string name(PopServiceName(rp)); 176 std::string name(PopServiceName(rp));
173 177
174 // Find the named port. 178 // Find the named port.
175 Kernel::KPort* port{}; 179 Kernel::KClientPort* client_port{};
176 auto port_result = service_manager.GetServicePort(&port, name); 180 auto port_result = service_manager.GetServicePort(&client_port, name);
177 if (port_result == Service::SM::ResultInvalidServiceName) { 181 if (port_result == Service::SM::ResultInvalidServiceName) {
178 LOG_ERROR(Service_SM, "Invalid service name '{}'", name); 182 LOG_ERROR(Service_SM, "Invalid service name '{}'", name);
179 return Service::SM::ResultInvalidServiceName; 183 return Service::SM::ResultInvalidServiceName;
@@ -187,7 +191,7 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
187 191
188 // Create a new session. 192 // Create a new session.
189 Kernel::KClientSession* session{}; 193 Kernel::KClientSession* session{};
190 if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) { 194 if (const auto result = client_port->CreateSession(&session); result.IsError()) {
191 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); 195 LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
192 return result; 196 return result;
193 } 197 }
@@ -221,7 +225,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
221 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, 225 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
222 max_session_count, is_light); 226 max_session_count, is_light);
223 227
224 if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr); 228 Kernel::KServerPort* server_port{};
229 if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
230 max_session_count, nullptr);
225 result.IsError()) { 231 result.IsError()) {
226 LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw); 232 LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
227 IPC::ResponseBuilder rb{ctx, 2}; 233 IPC::ResponseBuilder rb{ctx, 2};
@@ -229,13 +235,9 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
229 return; 235 return;
230 } 236 }
231 237
232 auto* port = Kernel::KPort::Create(kernel);
233 port->Initialize(ServerSessionCountMax, is_light, 0);
234 SCOPE_EXIT({ port->GetClientPort().Close(); });
235
236 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 238 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
237 rb.Push(ResultSuccess); 239 rb.Push(ResultSuccess);
238 rb.PushMoveObjects(port->GetServerPort()); 240 rb.PushMoveObjects(server_port);
239} 241}
240 242
241void SM::UnregisterService(HLERequestContext& ctx) { 243void SM::UnregisterService(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index ff74f588a..4ae32a9c1 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -56,10 +56,10 @@ public:
56 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
57 ~ServiceManager(); 57 ~ServiceManager();
58 58
59 Result RegisterService(std::string name, u32 max_sessions, 59 Result RegisterService(Kernel::KServerPort** out_server_port, std::string name,
60 SessionRequestHandlerFactory handler_factory); 60 u32 max_sessions, SessionRequestHandlerFactory handler_factory);
61 Result UnregisterService(const std::string& name); 61 Result UnregisterService(const std::string& name);
62 Result GetServicePort(Kernel::KPort** out_port, const std::string& name); 62 Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
63 63
64 template <Common::DerivedFrom<SessionRequestHandler> T> 64 template <Common::DerivedFrom<SessionRequestHandler> T>
65 std::shared_ptr<T> GetService(const std::string& service_name) const { 65 std::shared_ptr<T> GetService(const std::string& service_name) const {
@@ -84,7 +84,7 @@ private:
84 /// Map of registered services, retrieved using GetServicePort. 84 /// Map of registered services, retrieved using GetServicePort.
85 std::mutex lock; 85 std::mutex lock;
86 std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services; 86 std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
87 std::unordered_map<std::string, Kernel::KPort*> service_ports; 87 std::unordered_map<std::string, Kernel::KClientPort*> service_ports;
88 88
89 /// Kernel context 89 /// Kernel context
90 Kernel::KernelCore& kernel; 90 Kernel::KernelCore& kernel;
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 7dce28fe0..7f0fb91d0 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -28,7 +28,6 @@ void Controller::ConvertCurrentObjectToDomain(HLERequestContext& ctx) {
28void Controller::CloneCurrentObject(HLERequestContext& ctx) { 28void Controller::CloneCurrentObject(HLERequestContext& ctx) {
29 LOG_DEBUG(Service, "called"); 29 LOG_DEBUG(Service, "called");
30 30
31 auto& process = *ctx.GetThread().GetOwnerProcess();
32 auto session_manager = ctx.GetManager(); 31 auto session_manager = ctx.GetManager();
33 32
34 // FIXME: this is duplicated from the SVC, it should just call it instead 33 // FIXME: this is duplicated from the SVC, it should just call it instead
@@ -36,11 +35,11 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
36 35
37 // Reserve a new session from the process resource limit. 36 // Reserve a new session from the process resource limit.
38 Kernel::KScopedResourceReservation session_reservation( 37 Kernel::KScopedResourceReservation session_reservation(
39 &process, Kernel::LimitableResource::SessionCountMax); 38 Kernel::GetCurrentProcessPointer(kernel), Kernel::LimitableResource::SessionCountMax);
40 ASSERT(session_reservation.Succeeded()); 39 ASSERT(session_reservation.Succeeded());
41 40
42 // Create the session. 41 // Create the session.
43 Kernel::KSession* session = Kernel::KSession::Create(system.Kernel()); 42 Kernel::KSession* session = Kernel::KSession::Create(kernel);
44 ASSERT(session != nullptr); 43 ASSERT(session != nullptr);
45 44
46 // Initialize the session. 45 // Initialize the session.
@@ -50,7 +49,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
50 session_reservation.Commit(); 49 session_reservation.Commit();
51 50
52 // Register the session. 51 // Register the session.
53 Kernel::KSession::Register(system.Kernel(), session); 52 Kernel::KSession::Register(kernel, session);
54 53
55 // Register with server manager. 54 // Register with server manager.
56 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(), 55 session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 60ee78e89..c9f8707b7 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -129,9 +129,10 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
129 } 129 }
130 metadata.Print(); 130 metadata.Print();
131 131
132 // Enable NCE only for programs with 39-bit address space. 132 // Enable NCE only for applications with 39-bit address space.
133 const bool is_39bit = 133 const bool is_39bit =
134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; 134 metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit;
135 const bool is_application = metadata.GetPoolPartition() == FileSys::PoolPartition::Application;
135 Settings::SetNceEnabled(is_39bit); 136 Settings::SetNceEnabled(is_39bit);
136 137
137 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", 138 const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2",
@@ -147,7 +148,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
147 148
148 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { 149 const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* {
149#ifdef HAS_NCE 150#ifdef HAS_NCE
150 if (Settings::IsNceEnabled()) { 151 if (is_application && Settings::IsNceEnabled()) {
151 return &module_patchers[i]; 152 return &module_patchers[i];
152 } 153 }
153#endif 154#endif
@@ -175,7 +176,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
175 176
176 // Enable direct memory mapping in case of NCE. 177 // Enable direct memory mapping in case of NCE.
177 const u64 fastmem_base = [&]() -> size_t { 178 const u64 fastmem_base = [&]() -> size_t {
178 if (Settings::IsNceEnabled()) { 179 if (is_application && Settings::IsNceEnabled()) {
179 auto& buffer = system.DeviceMemory().buffer; 180 auto& buffer = system.DeviceMemory().buffer;
180 buffer.EnableDirectMappedAddress(); 181 buffer.EnableDirectMappedAddress();
181 return reinterpret_cast<u64>(buffer.VirtualBasePointer()); 182 return reinterpret_cast<u64>(buffer.VirtualBasePointer());
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 169bf4c8c..8176a41be 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/atomic_ops.h" 11#include "common/atomic_ops.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/heap_tracker.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
14#include "common/page_table.h" 15#include "common/page_table.h"
15#include "common/scope_exit.h" 16#include "common/scope_exit.h"
@@ -45,11 +46,25 @@ struct Memory::Impl {
45 46
46 void SetCurrentPageTable(Kernel::KProcess& process) { 47 void SetCurrentPageTable(Kernel::KProcess& process) {
47 current_page_table = &process.GetPageTable().GetImpl(); 48 current_page_table = &process.GetPageTable().GetImpl();
48 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); 49
50 if (std::addressof(process) == system.ApplicationProcess() &&
51 Settings::IsFastmemEnabled()) {
52 current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
53 } else {
54 current_page_table->fastmem_arena = nullptr;
55 }
56
57#ifdef __linux__
58 heap_tracker.emplace(system.DeviceMemory().buffer);
59 buffer = std::addressof(*heap_tracker);
60#else
61 buffer = std::addressof(system.DeviceMemory().buffer);
62#endif
49 } 63 }
50 64
51 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 65 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
52 Common::PhysicalAddress target, Common::MemoryPermission perms) { 66 Common::PhysicalAddress target, Common::MemoryPermission perms,
67 bool separate_heap) {
53 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 68 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
54 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 69 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
55 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", 70 ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}",
@@ -57,20 +72,21 @@ struct Memory::Impl {
57 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target, 72 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
58 Common::PageType::Memory); 73 Common::PageType::Memory);
59 74
60 if (Settings::IsFastmemEnabled()) { 75 if (current_page_table->fastmem_arena) {
61 system.DeviceMemory().buffer.Map(GetInteger(base), 76 buffer->Map(GetInteger(base), GetInteger(target) - DramMemoryMap::Base, size, perms,
62 GetInteger(target) - DramMemoryMap::Base, size, perms); 77 separate_heap);
63 } 78 }
64 } 79 }
65 80
66 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 81 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
82 bool separate_heap) {
67 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 83 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
68 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base)); 84 ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", GetInteger(base));
69 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0, 85 MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
70 Common::PageType::Unmapped); 86 Common::PageType::Unmapped);
71 87
72 if (Settings::IsFastmemEnabled()) { 88 if (current_page_table->fastmem_arena) {
73 system.DeviceMemory().buffer.Unmap(GetInteger(base), size); 89 buffer->Unmap(GetInteger(base), size, separate_heap);
74 } 90 }
75 } 91 }
76 92
@@ -79,17 +95,7 @@ struct Memory::Impl {
79 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size); 95 ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
80 ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr); 96 ASSERT_MSG((vaddr & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", vaddr);
81 97
82 if (!Settings::IsFastmemEnabled()) { 98 if (!current_page_table->fastmem_arena) {
83 return;
84 }
85
86 const bool is_r = True(perms & Common::MemoryPermission::Read);
87 const bool is_w = True(perms & Common::MemoryPermission::Write);
88 const bool is_x =
89 True(perms & Common::MemoryPermission::Execute) && Settings::IsNceEnabled();
90
91 if (!current_page_table) {
92 system.DeviceMemory().buffer.Protect(vaddr, size, is_r, is_w, is_x);
93 return; 99 return;
94 } 100 }
95 101
@@ -101,8 +107,7 @@ struct Memory::Impl {
101 switch (page_type) { 107 switch (page_type) {
102 case Common::PageType::RasterizerCachedMemory: 108 case Common::PageType::RasterizerCachedMemory:
103 if (protect_bytes > 0) { 109 if (protect_bytes > 0) {
104 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, 110 buffer->Protect(protect_begin, protect_bytes, perms);
105 is_x);
106 protect_bytes = 0; 111 protect_bytes = 0;
107 } 112 }
108 break; 113 break;
@@ -115,7 +120,7 @@ struct Memory::Impl {
115 } 120 }
116 121
117 if (protect_bytes > 0) { 122 if (protect_bytes > 0) {
118 system.DeviceMemory().buffer.Protect(protect_begin, protect_bytes, is_r, is_w, is_x); 123 buffer->Protect(protect_begin, protect_bytes, perms);
119 } 124 }
120 } 125 }
121 126
@@ -239,7 +244,7 @@ struct Memory::Impl {
239 244
240 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, 245 bool WalkBlock(const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped,
241 auto on_memory, auto on_rasterizer, auto increment) { 246 auto on_memory, auto on_rasterizer, auto increment) {
242 const auto& page_table = system.ApplicationProcess()->GetPageTable().GetImpl(); 247 const auto& page_table = *current_page_table;
243 std::size_t remaining_size = size; 248 std::size_t remaining_size = size;
244 std::size_t page_index = addr >> YUZU_PAGEBITS; 249 std::size_t page_index = addr >> YUZU_PAGEBITS;
245 std::size_t page_offset = addr & YUZU_PAGEMASK; 250 std::size_t page_offset = addr & YUZU_PAGEMASK;
@@ -484,8 +489,10 @@ struct Memory::Impl {
484 return; 489 return;
485 } 490 }
486 491
487 if (Settings::IsFastmemEnabled()) { 492 if (current_page_table->fastmem_arena) {
488 system.DeviceMemory().buffer.Protect(vaddr, size, !debug, !debug); 493 const auto perm{debug ? Common::MemoryPermission{}
494 : Common::MemoryPermission::ReadWrite};
495 buffer->Protect(vaddr, size, perm);
489 } 496 }
490 497
491 // Iterate over a contiguous CPU address space, marking/unmarking the region. 498 // Iterate over a contiguous CPU address space, marking/unmarking the region.
@@ -541,10 +548,15 @@ struct Memory::Impl {
541 return; 548 return;
542 } 549 }
543 550
544 if (Settings::IsFastmemEnabled()) { 551 if (current_page_table->fastmem_arena) {
545 const bool is_read_enable = 552 Common::MemoryPermission perm{};
546 !Settings::values.use_reactive_flushing.GetValue() || !cached; 553 if (!Settings::values.use_reactive_flushing.GetValue() || !cached) {
547 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 554 perm |= Common::MemoryPermission::Read;
555 }
556 if (!cached) {
557 perm |= Common::MemoryPermission::Write;
558 }
559 buffer->Protect(vaddr, size, perm);
548 } 560 }
549 561
550 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU 562 // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
@@ -855,6 +867,13 @@ struct Memory::Impl {
855 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{}; 867 std::array<GPUDirtyState, Core::Hardware::NUM_CPU_CORES> rasterizer_write_areas{};
856 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers; 868 std::span<Core::GPUDirtyMemoryManager> gpu_dirty_managers;
857 std::mutex sys_core_guard; 869 std::mutex sys_core_guard;
870
871 std::optional<Common::HeapTracker> heap_tracker;
872#ifdef __linux__
873 Common::HeapTracker* buffer{};
874#else
875 Common::HostMemory* buffer{};
876#endif
858}; 877};
859 878
860Memory::Memory(Core::System& system_) : system{system_} { 879Memory::Memory(Core::System& system_) : system{system_} {
@@ -872,12 +891,14 @@ void Memory::SetCurrentPageTable(Kernel::KProcess& process) {
872} 891}
873 892
874void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 893void Memory::MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
875 Common::PhysicalAddress target, Common::MemoryPermission perms) { 894 Common::PhysicalAddress target, Common::MemoryPermission perms,
876 impl->MapMemoryRegion(page_table, base, size, target, perms); 895 bool separate_heap) {
896 impl->MapMemoryRegion(page_table, base, size, target, perms, separate_heap);
877} 897}
878 898
879void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size) { 899void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
880 impl->UnmapRegion(page_table, base, size); 900 bool separate_heap) {
901 impl->UnmapRegion(page_table, base, size, separate_heap);
881} 902}
882 903
883void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size, 904void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress vaddr, u64 size,
@@ -886,8 +907,7 @@ void Memory::ProtectRegion(Common::PageTable& page_table, Common::ProcessAddress
886} 907}
887 908
888bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { 909bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const {
889 const Kernel::KProcess& process = *system.ApplicationProcess(); 910 const auto& page_table = *impl->current_page_table;
890 const auto& page_table = process.GetPageTable().GetImpl();
891 const size_t page = vaddr >> YUZU_PAGEBITS; 911 const size_t page = vaddr >> YUZU_PAGEBITS;
892 if (page >= page_table.pointers.size()) { 912 if (page >= page_table.pointers.size()) {
893 return false; 913 return false;
@@ -1048,7 +1068,9 @@ void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) {
1048} 1068}
1049 1069
1050bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) { 1070bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1051 bool mapped = true; 1071 [[maybe_unused]] bool mapped = true;
1072 [[maybe_unused]] bool rasterizer = false;
1073
1052 u8* const ptr = impl->GetPointerImpl( 1074 u8* const ptr = impl->GetPointerImpl(
1053 GetInteger(vaddr), 1075 GetInteger(vaddr),
1054 [&] { 1076 [&] {
@@ -1056,8 +1078,26 @@ bool Memory::InvalidateNCE(Common::ProcessAddress vaddr, size_t size) {
1056 GetInteger(vaddr)); 1078 GetInteger(vaddr));
1057 mapped = false; 1079 mapped = false;
1058 }, 1080 },
1059 [&] { impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size); }); 1081 [&] {
1082 impl->system.GPU().InvalidateRegion(GetInteger(vaddr), size);
1083 rasterizer = true;
1084 });
1085
1086#ifdef __linux__
1087 if (!rasterizer && mapped) {
1088 impl->buffer->DeferredMapSeparateHeap(GetInteger(vaddr));
1089 }
1090#endif
1091
1060 return mapped && ptr != nullptr; 1092 return mapped && ptr != nullptr;
1061} 1093}
1062 1094
1095bool Memory::InvalidateSeparateHeap(void* fault_address) {
1096#ifdef __linux__
1097 return impl->buffer->DeferredMapSeparateHeap(static_cast<u8*>(fault_address));
1098#else
1099 return false;
1100#endif
1101}
1102
1063} // namespace Core::Memory 1103} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index c1879e78f..dddfaf4a4 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -86,7 +86,8 @@ public:
86 * @param perms The permissions to map the memory with. 86 * @param perms The permissions to map the memory with.
87 */ 87 */
88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size, 88 void MapMemoryRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
89 Common::PhysicalAddress target, Common::MemoryPermission perms); 89 Common::PhysicalAddress target, Common::MemoryPermission perms,
90 bool separate_heap);
90 91
91 /** 92 /**
92 * Unmaps a region of the emulated process address space. 93 * Unmaps a region of the emulated process address space.
@@ -95,7 +96,8 @@ public:
95 * @param base The address to begin unmapping at. 96 * @param base The address to begin unmapping at.
96 * @param size The amount of bytes to unmap. 97 * @param size The amount of bytes to unmap.
97 */ 98 */
98 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size); 99 void UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress base, u64 size,
100 bool separate_heap);
99 101
100 /** 102 /**
101 * Protects a region of the emulated process address space with the new permissions. 103 * Protects a region of the emulated process address space with the new permissions.
@@ -486,6 +488,7 @@ public:
486 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); 488 void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers);
487 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); 489 void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size);
488 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size); 490 bool InvalidateNCE(Common::ProcessAddress vaddr, size_t size);
491 bool InvalidateSeparateHeap(void* fault_address);
489 void FlushRegion(Common::ProcessAddress dest_addr, size_t size); 492 void FlushRegion(Common::ProcessAddress dest_addr, size_t size);
490 493
491private: 494private:
@@ -683,7 +686,8 @@ public:
683 } else { 686 } else {
684 this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes()); 687 this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
685 } 688 }
686 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { 689 } else if constexpr ((FLAGS & GuestMemoryFlags::Safe) ||
690 (FLAGS & GuestMemoryFlags::Cached)) {
687 this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes()); 691 this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
688 } 692 }
689 } 693 }
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 3fc4024dc..7bc5b5ae5 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -190,15 +190,15 @@ CheatEngine::CheatEngine(System& system_, std::vector<CheatEntry> cheats_,
190} 190}
191 191
192CheatEngine::~CheatEngine() { 192CheatEngine::~CheatEngine() {
193 core_timing.UnscheduleEvent(event, 0); 193 core_timing.UnscheduleEvent(event);
194} 194}
195 195
196void CheatEngine::Initialize() { 196void CheatEngine::Initialize() {
197 event = Core::Timing::CreateEvent( 197 event = Core::Timing::CreateEvent(
198 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 198 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
199 [this](std::uintptr_t user_data, s64 time, 199 [this](s64 time,
200 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 200 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
201 FrameCallback(user_data, ns_late); 201 FrameCallback(ns_late);
202 return std::nullopt; 202 return std::nullopt;
203 }); 203 });
204 core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event); 204 core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
@@ -239,7 +239,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> reload_cheats) {
239 239
240MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 240MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
241 241
242void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 242void CheatEngine::FrameCallback(std::chrono::nanoseconds ns_late) {
243 if (is_pending_reload.exchange(false)) { 243 if (is_pending_reload.exchange(false)) {
244 vm.LoadProgram(cheats); 244 vm.LoadProgram(cheats);
245 } 245 }
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index 284abdd28..ced2168d1 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -70,7 +70,7 @@ public:
70 void Reload(std::vector<CheatEntry> reload_cheats); 70 void Reload(std::vector<CheatEntry> reload_cheats);
71 71
72private: 72private:
73 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 73 void FrameCallback(std::chrono::nanoseconds ns_late);
74 74
75 DmntCheatVm vm; 75 DmntCheatVm vm;
76 CheatProcessMetadata metadata; 76 CheatProcessMetadata metadata;
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 98ebbbf32..9d42c726e 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -51,18 +51,17 @@ void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 v
51 51
52Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_) 52Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)
53 : core_timing{core_timing_}, memory{memory_} { 53 : core_timing{core_timing_}, memory{memory_} {
54 event = Core::Timing::CreateEvent( 54 event = Core::Timing::CreateEvent("MemoryFreezer::FrameCallback",
55 "MemoryFreezer::FrameCallback", 55 [this](s64 time, std::chrono::nanoseconds ns_late)
56 [this](std::uintptr_t user_data, s64 time, 56 -> std::optional<std::chrono::nanoseconds> {
57 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 57 FrameCallback(ns_late);
58 FrameCallback(user_data, ns_late); 58 return std::nullopt;
59 return std::nullopt; 59 });
60 });
61 core_timing.ScheduleEvent(memory_freezer_ns, event); 60 core_timing.ScheduleEvent(memory_freezer_ns, event);
62} 61}
63 62
64Freezer::~Freezer() { 63Freezer::~Freezer() {
65 core_timing.UnscheduleEvent(event, 0); 64 core_timing.UnscheduleEvent(event);
66} 65}
67 66
68void Freezer::SetActive(bool is_active) { 67void Freezer::SetActive(bool is_active) {
@@ -159,7 +158,7 @@ Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const {
159 [address](const Entry& entry) { return entry.address == address; }); 158 [address](const Entry& entry) { return entry.address == address; });
160} 159}
161 160
162void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { 161void Freezer::FrameCallback(std::chrono::nanoseconds ns_late) {
163 if (!IsActive()) { 162 if (!IsActive()) {
164 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
165 return; 164 return;
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 0d6df5217..2efbc11f3 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -77,7 +77,7 @@ private:
77 Entries::iterator FindEntry(VAddr address); 77 Entries::iterator FindEntry(VAddr address);
78 Entries::const_iterator FindEntry(VAddr address) const; 78 Entries::const_iterator FindEntry(VAddr address) const;
79 79
80 void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 80 void FrameCallback(std::chrono::nanoseconds ns_late);
81 void FillEntryReads(); 81 void FillEntryReads();
82 82
83 std::atomic_bool active{false}; 83 std::atomic_bool active{false};
diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp
index d9f99148b..20de91ff4 100644
--- a/src/frontend_common/config.cpp
+++ b/src/frontend_common/config.cpp
@@ -403,59 +403,63 @@ void Config::SavePlayerValues(const std::size_t player_index) {
403 // No custom profile selected 403 // No custom profile selected
404 return; 404 return;
405 } 405 }
406 WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name, 406 WriteStringSetting(std::string(player_prefix).append("profile_name"), player.profile_name,
407 std::make_optional(std::string(""))); 407 std::make_optional(std::string("")));
408 } 408 }
409 409
410 WriteSetting(std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type), 410 WriteIntegerSetting(
411 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); 411 std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type),
412 std::make_optional(static_cast<u8>(Settings::ControllerType::ProController)));
412 413
413 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { 414 if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) {
414 WriteSetting(std::string(player_prefix).append("connected"), player.connected, 415 WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected,
415 std::make_optional(player_index == 0)); 416 std::make_optional(player_index == 0));
416 WriteSetting(std::string(player_prefix).append("vibration_enabled"), 417 WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"),
417 player.vibration_enabled, std::make_optional(true)); 418 player.vibration_enabled, std::make_optional(true));
418 WriteSetting(std::string(player_prefix).append("vibration_strength"), 419 WriteIntegerSetting(std::string(player_prefix).append("vibration_strength"),
419 player.vibration_strength, std::make_optional(100)); 420 player.vibration_strength, std::make_optional(100));
420 WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left, 421 WriteIntegerSetting(std::string(player_prefix).append("body_color_left"),
421 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); 422 player.body_color_left,
422 WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right, 423 std::make_optional(Settings::JOYCON_BODY_NEON_BLUE));
423 std::make_optional(Settings::JOYCON_BODY_NEON_RED)); 424 WriteIntegerSetting(std::string(player_prefix).append("body_color_right"),
424 WriteSetting(std::string(player_prefix).append("button_color_left"), 425 player.body_color_right,
425 player.button_color_left, 426 std::make_optional(Settings::JOYCON_BODY_NEON_RED));
426 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); 427 WriteIntegerSetting(std::string(player_prefix).append("button_color_left"),
427 WriteSetting(std::string(player_prefix).append("button_color_right"), 428 player.button_color_left,
428 player.button_color_right, 429 std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE));
429 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); 430 WriteIntegerSetting(std::string(player_prefix).append("button_color_right"),
431 player.button_color_right,
432 std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED));
430 } 433 }
431} 434}
432 435
433void Config::SaveTouchscreenValues() { 436void Config::SaveTouchscreenValues() {
434 const auto& touchscreen = Settings::values.touchscreen; 437 const auto& touchscreen = Settings::values.touchscreen;
435 438
436 WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true)); 439 WriteBooleanSetting(std::string("touchscreen_enabled"), touchscreen.enabled,
440 std::make_optional(true));
437 441
438 WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, 442 WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle,
439 std::make_optional(static_cast<u32>(0))); 443 std::make_optional(static_cast<u32>(0)));
440 WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, 444 WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x,
441 std::make_optional(static_cast<u32>(15))); 445 std::make_optional(static_cast<u32>(15)));
442 WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, 446 WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
443 std::make_optional(static_cast<u32>(15))); 447 std::make_optional(static_cast<u32>(15)));
444} 448}
445 449
446void Config::SaveMotionTouchValues() { 450void Config::SaveMotionTouchValues() {
447 BeginArray(std::string("touch_from_button_maps")); 451 BeginArray(std::string("touch_from_button_maps"));
448 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { 452 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
449 SetArrayIndex(static_cast<int>(p)); 453 SetArrayIndex(static_cast<int>(p));
450 WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, 454 WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name,
451 std::make_optional(std::string("default"))); 455 std::make_optional(std::string("default")));
452 456
453 BeginArray(std::string("entries")); 457 BeginArray(std::string("entries"));
454 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); 458 for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
455 ++q) { 459 ++q) {
456 SetArrayIndex(static_cast<int>(q)); 460 SetArrayIndex(static_cast<int>(q));
457 WriteSetting(std::string("bind"), 461 WriteStringSetting(std::string("bind"),
458 Settings::values.touch_from_button_maps[p].buttons[q]); 462 Settings::values.touch_from_button_maps[p].buttons[q]);
459 } 463 }
460 EndArray(); // entries 464 EndArray(); // entries
461 } 465 }
@@ -520,16 +524,16 @@ void Config::SaveCoreValues() {
520void Config::SaveDataStorageValues() { 524void Config::SaveDataStorageValues() {
521 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); 525 BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage));
522 526
523 WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), 527 WriteStringSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir),
524 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); 528 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
525 WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), 529 WriteStringSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir),
526 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); 530 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
527 WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), 531 WriteStringSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir),
528 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); 532 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
529 WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), 533 WriteStringSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir),
530 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); 534 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
531 WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), 535 WriteStringSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir),
532 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); 536 std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir)));
533 537
534 WriteCategory(Settings::Category::DataStorage); 538 WriteCategory(Settings::Category::DataStorage);
535 539
@@ -540,7 +544,7 @@ void Config::SaveDebuggingValues() {
540 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); 544 BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging));
541 545
542 // Intentionally not using the QT default setting as this is intended to be changed in the ini 546 // Intentionally not using the QT default setting as this is intended to be changed in the ini
543 WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times); 547 WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
544 548
545 WriteCategory(Settings::Category::Debugging); 549 WriteCategory(Settings::Category::Debugging);
546 WriteCategory(Settings::Category::DebuggingGraphics); 550 WriteCategory(Settings::Category::DebuggingGraphics);
@@ -564,11 +568,13 @@ void Config::SaveDisabledAddOnValues() {
564 BeginArray(std::string("")); 568 BeginArray(std::string(""));
565 for (const auto& elem : Settings::values.disabled_addons) { 569 for (const auto& elem : Settings::values.disabled_addons) {
566 SetArrayIndex(i); 570 SetArrayIndex(i);
567 WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast<u64>(0))); 571 WriteIntegerSetting(std::string("title_id"), elem.first,
572 std::make_optional(static_cast<u64>(0)));
568 BeginArray(std::string("disabled")); 573 BeginArray(std::string("disabled"));
569 for (std::size_t j = 0; j < elem.second.size(); ++j) { 574 for (std::size_t j = 0; j < elem.second.size(); ++j) {
570 SetArrayIndex(static_cast<int>(j)); 575 SetArrayIndex(static_cast<int>(j));
571 WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); 576 WriteStringSetting(std::string("d"), elem.second[j],
577 std::make_optional(std::string("")));
572 } 578 }
573 EndArray(); // disabled 579 EndArray(); // disabled
574 ++i; 580 ++i;
@@ -609,8 +615,8 @@ void Config::SaveRendererValues() {
609void Config::SaveScreenshotValues() { 615void Config::SaveScreenshotValues() {
610 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); 616 BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots));
611 617
612 WriteSetting(std::string("screenshot_path"), 618 WriteStringSetting(std::string("screenshot_path"),
613 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); 619 FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir));
614 WriteCategory(Settings::Category::Screenshots); 620 WriteCategory(Settings::Category::Screenshots);
615 621
616 EndGroup(); 622 EndGroup();
@@ -746,46 +752,59 @@ bool Config::Exists(const std::string& section, const std::string& key) const {
746 return !value.empty(); 752 return !value.empty();
747} 753}
748 754
749template <typename Type> 755void Config::WriteBooleanSetting(const std::string& key, const bool& value,
750void Config::WriteSetting(const std::string& key, const Type& value, 756 const std::optional<bool>& default_value,
751 const std::optional<Type>& default_value, 757 const std::optional<bool>& use_global) {
752 const std::optional<bool>& use_global) { 758 std::optional<std::string> string_default = std::nullopt;
753 std::string full_key = GetFullKey(key, false); 759 if (default_value.has_value()) {
760 string_default = std::make_optional(ToString(default_value.value()));
761 }
762 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
763}
754 764
755 std::string saved_value; 765void Config::WriteDoubleSetting(const std::string& key, const double& value,
756 std::string string_default; 766 const std::optional<double>& default_value,
757 if constexpr (std::is_same_v<Type, std::string>) { 767 const std::optional<bool>& use_global) {
758 saved_value.append(AdjustOutputString(value)); 768 std::optional<std::string> string_default = std::nullopt;
759 if (default_value.has_value()) { 769 if (default_value.has_value()) {
760 string_default.append(AdjustOutputString(default_value.value())); 770 string_default = std::make_optional(ToString(default_value.value()));
761 }
762 } else {
763 saved_value.append(AdjustOutputString(ToString(value)));
764 if (default_value.has_value()) {
765 string_default.append(ToString(default_value.value()));
766 }
767 } 771 }
772 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
773}
768 774
769 if (default_value.has_value() && use_global.has_value()) { 775void Config::WriteStringSetting(const std::string& key, const std::string& value,
776 const std::optional<std::string>& default_value,
777 const std::optional<bool>& use_global) {
778 std::optional string_default = default_value;
779 if (default_value.has_value()) {
780 string_default.value().append(AdjustOutputString(default_value.value()));
781 }
782 WritePreparedSetting(key, AdjustOutputString(value), string_default, use_global);
783}
784
785void Config::WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
786 const std::optional<std::string>& adjusted_default_value,
787 const std::optional<bool>& use_global) {
788 std::string full_key = GetFullKey(key, false);
789 if (adjusted_default_value.has_value() && use_global.has_value()) {
770 if (!global) { 790 if (!global) {
771 WriteSettingInternal(std::string(full_key).append("\\global"), 791 WriteString(std::string(full_key).append("\\global"), ToString(use_global.value()));
772 ToString(use_global.value()));
773 } 792 }
774 if (global || use_global.value() == false) { 793 if (global || use_global.value() == false) {
775 WriteSettingInternal(std::string(full_key).append("\\default"), 794 WriteString(std::string(full_key).append("\\default"),
776 ToString(string_default == saved_value)); 795 ToString(adjusted_default_value == adjusted_value));
777 WriteSettingInternal(full_key, saved_value); 796 WriteString(full_key, adjusted_value);
778 } 797 }
779 } else if (default_value.has_value() && !use_global.has_value()) { 798 } else if (adjusted_default_value.has_value() && !use_global.has_value()) {
780 WriteSettingInternal(std::string(full_key).append("\\default"), 799 WriteString(std::string(full_key).append("\\default"),
781 ToString(string_default == saved_value)); 800 ToString(adjusted_default_value == adjusted_value));
782 WriteSettingInternal(full_key, saved_value); 801 WriteString(full_key, adjusted_value);
783 } else { 802 } else {
784 WriteSettingInternal(full_key, saved_value); 803 WriteString(full_key, adjusted_value);
785 } 804 }
786} 805}
787 806
788void Config::WriteSettingInternal(const std::string& key, const std::string& value) { 807void Config::WriteString(const std::string& key, const std::string& value) {
789 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); 808 config->SetValue(GetSection().c_str(), key.c_str(), value.c_str());
790} 809}
791 810
@@ -861,17 +880,18 @@ void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) {
861 std::string key = AdjustKey(setting->GetLabel()); 880 std::string key = AdjustKey(setting->GetLabel());
862 if (setting->Switchable()) { 881 if (setting->Switchable()) {
863 if (!global) { 882 if (!global) {
864 WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); 883 WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal());
865 } 884 }
866 if (global || !setting->UsingGlobal()) { 885 if (global || !setting->UsingGlobal()) {
867 WriteSetting(std::string(key).append("\\default"), 886 auto value = global ? setting->ToStringGlobal() : setting->ToString();
868 setting->ToString() == setting->DefaultToString()); 887 WriteBooleanSetting(std::string(key).append("\\default"),
869 WriteSetting(key, setting->ToString()); 888 value == setting->DefaultToString());
889 WriteStringSetting(key, value);
870 } 890 }
871 } else if (global) { 891 } else if (global) {
872 WriteSetting(std::string(key).append("\\default"), 892 WriteBooleanSetting(std::string(key).append("\\default"),
873 setting->ToString() == setting->DefaultToString()); 893 setting->ToString() == setting->DefaultToString());
874 WriteSetting(key, setting->ToString()); 894 WriteStringSetting(key, setting->ToString());
875 } 895 }
876} 896}
877 897
diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h
index b3812af17..b01631649 100644
--- a/src/frontend_common/config.h
+++ b/src/frontend_common/config.h
@@ -154,11 +154,26 @@ protected:
154 * @param use_global Specifies if the custom or global config should be in use, for custom 154 * @param use_global Specifies if the custom or global config should be in use, for custom
155 * configs 155 * configs
156 */ 156 */
157 template <typename Type = int> 157 void WriteBooleanSetting(const std::string& key, const bool& value,
158 void WriteSetting(const std::string& key, const Type& value, 158 const std::optional<bool>& default_value = std::nullopt,
159 const std::optional<Type>& default_value = std::nullopt, 159 const std::optional<bool>& use_global = std::nullopt);
160 const std::optional<bool>& use_global = std::nullopt); 160 void WriteDoubleSetting(const std::string& key, const double& value,
161 void WriteSettingInternal(const std::string& key, const std::string& value); 161 const std::optional<double>& default_value = std::nullopt,
162 const std::optional<bool>& use_global = std::nullopt);
163 void WriteStringSetting(const std::string& key, const std::string& value,
164 const std::optional<std::string>& default_value = std::nullopt,
165 const std::optional<bool>& use_global = std::nullopt);
166 template <typename T>
167 std::enable_if_t<std::is_integral_v<T>> WriteIntegerSetting(
168 const std::string& key, const T& value,
169 const std::optional<T>& default_value = std::nullopt,
170 const std::optional<bool>& use_global = std::nullopt) {
171 std::optional<std::string> string_default = std::nullopt;
172 if (default_value.has_value()) {
173 string_default = std::make_optional(ToString(default_value.value()));
174 }
175 WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global);
176 }
162 177
163 void ReadCategory(Settings::Category category); 178 void ReadCategory(Settings::Category category);
164 void WriteCategory(Settings::Category category); 179 void WriteCategory(Settings::Category category);
@@ -175,8 +190,10 @@ protected:
175 return value_ ? "true" : "false"; 190 return value_ ? "true" : "false";
176 } else if constexpr (std::is_same_v<T, u64>) { 191 } else if constexpr (std::is_same_v<T, u64>) {
177 return std::to_string(static_cast<u64>(value_)); 192 return std::to_string(static_cast<u64>(value_));
178 } else { 193 } else if constexpr (std::is_same_v<T, s64>) {
179 return std::to_string(static_cast<s64>(value_)); 194 return std::to_string(static_cast<s64>(value_));
195 } else {
196 return std::to_string(value_);
180 } 197 }
181 } 198 }
182 199
@@ -197,9 +214,13 @@ protected:
197 const bool global; 214 const bool global;
198 215
199private: 216private:
200 inline static std::array<char, 19> special_characters = {'!', '#', '$', '%', '^', '&', '*', 217 void WritePreparedSetting(const std::string& key, const std::string& adjusted_value,
201 '|', ';', '\'', '\"', ',', '<', '.', 218 const std::optional<std::string>& adjusted_default_value,
202 '>', '?', '`', '~', '='}; 219 const std::optional<bool>& use_global);
220 void WriteString(const std::string& key, const std::string& value);
221
222 inline static std::array<char, 18> special_characters = {
223 '!', '#', '$', '%', '^', '&', '*', '|', ';', '\'', '\"', ',', '<', '>', '?', '`', '~', '='};
203 224
204 struct ConfigArray { 225 struct ConfigArray {
205 std::string name; 226 std::string name;
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 e5a78a914..feca5105f 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
@@ -74,6 +74,11 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
74 case IR::Attribute::ClipDistance7: { 74 case IR::Attribute::ClipDistance7: {
75 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)}; 75 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
76 const u32 index{static_cast<u32>(attr) - base}; 76 const u32 index{static_cast<u32>(attr) - base};
77 if (index >= ctx.profile.max_user_clip_distances) {
78 LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
79 ctx.profile.max_user_clip_distances);
80 return std::nullopt;
81 }
77 const Id clip_num{ctx.Const(index)}; 82 const Id clip_num{ctx.Const(index)};
78 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num); 83 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
79 } 84 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 22ceca19c..800754554 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -214,16 +214,16 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
214 } 214 }
215} 215}
216 216
217Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { 217std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
218 if (!index.IsImmediate() || index.U32() != 0) { 218 if (!index.IsImmediate() || index.U32() != 0) {
219 throw NotImplementedException("Indirect image indexing"); 219 throw NotImplementedException("Indirect image indexing");
220 } 220 }
221 if (info.type == TextureType::Buffer) { 221 if (info.type == TextureType::Buffer) {
222 const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; 222 const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
223 return ctx.OpLoad(def.image_type, def.id); 223 return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
224 } else { 224 } else {
225 const ImageDefinition def{ctx.images.at(info.descriptor_index)}; 225 const ImageDefinition def{ctx.images.at(info.descriptor_index)};
226 return ctx.OpLoad(def.image_type, def.id); 226 return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
227 } 227 }
228} 228}
229 229
@@ -566,13 +566,23 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
566 LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host"); 566 LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
567 return ctx.ConstantNull(ctx.U32[4]); 567 return ctx.ConstantNull(ctx.U32[4]);
568 } 568 }
569 return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4], 569 const auto [image, is_integer] = Image(ctx, index, info);
570 Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{}); 570 const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
571 Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
572 result_type, image, coords, std::nullopt, std::span<const Id>{})};
573 if (!is_integer) {
574 color = ctx.OpBitcast(ctx.U32[4], color);
575 }
576 return color;
571} 577}
572 578
573void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) { 579void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
574 const auto info{inst->Flags<IR::TextureInstInfo>()}; 580 const auto info{inst->Flags<IR::TextureInstInfo>()};
575 ctx.OpImageWrite(Image(ctx, index, info), coords, color); 581 const auto [image, is_integer] = Image(ctx, index, info);
582 if (!is_integer) {
583 color = ctx.OpBitcast(ctx.F32[4], color);
584 }
585 ctx.OpImageWrite(image, coords, color);
576} 586}
577 587
578Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) { 588Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index eb3cc23cc..0442adc83 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -74,20 +74,19 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
74 throw InvalidArgument("Invalid image format {}", format); 74 throw InvalidArgument("Invalid image format {}", format);
75} 75}
76 76
77Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { 77Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
78 const spv::ImageFormat format{GetImageFormat(desc.format)}; 78 const spv::ImageFormat format{GetImageFormat(desc.format)};
79 const Id type{ctx.U32[1]};
80 switch (desc.type) { 79 switch (desc.type) {
81 case TextureType::Color1D: 80 case TextureType::Color1D:
82 return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format); 81 return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
83 case TextureType::ColorArray1D: 82 case TextureType::ColorArray1D:
84 return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format); 83 return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
85 case TextureType::Color2D: 84 case TextureType::Color2D:
86 return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format); 85 return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
87 case TextureType::ColorArray2D: 86 case TextureType::ColorArray2D:
88 return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format); 87 return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format);
89 case TextureType::Color3D: 88 case TextureType::Color3D:
90 return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format); 89 return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
91 case TextureType::Buffer: 90 case TextureType::Buffer:
92 throw NotImplementedException("Image buffer"); 91 throw NotImplementedException("Image buffer");
93 default: 92 default:
@@ -97,9 +96,9 @@ Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
97} 96}
98 97
99Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin, 98Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
100 spv::StorageClass storage_class) { 99 spv::StorageClass storage_class, std::optional<Id> initializer = std::nullopt) {
101 const Id pointer_type{ctx.TypePointer(storage_class, type)}; 100 const Id pointer_type{ctx.TypePointer(storage_class, type)};
102 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)}; 101 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class, initializer)};
103 if (builtin) { 102 if (builtin) {
104 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin); 103 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
105 } 104 }
@@ -145,11 +144,12 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
145} 144}
146 145
147Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations, 146Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
148 std::optional<spv::BuiltIn> builtin = std::nullopt) { 147 std::optional<spv::BuiltIn> builtin = std::nullopt,
148 std::optional<Id> initializer = std::nullopt) {
149 if (invocations && ctx.stage == Stage::TessellationControl) { 149 if (invocations && ctx.stage == Stage::TessellationControl) {
150 type = ctx.TypeArray(type, ctx.Const(*invocations)); 150 type = ctx.TypeArray(type, ctx.Const(*invocations));
151 } 151 }
152 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); 152 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output, initializer);
153} 153}
154 154
155void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) { 155void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
@@ -812,10 +812,14 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
812 labels.push_back(OpLabel()); 812 labels.push_back(OpLabel());
813 } 813 }
814 if (info.stores.ClipDistances()) { 814 if (info.stores.ClipDistances()) {
815 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2); 815 if (profile.max_user_clip_distances >= 4) {
816 labels.push_back(OpLabel()); 816 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
817 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2); 817 labels.push_back(OpLabel());
818 labels.push_back(OpLabel()); 818 }
819 if (profile.max_user_clip_distances >= 8) {
820 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
821 labels.push_back(OpLabel());
822 }
819 } 823 }
820 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone); 824 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
821 OpSwitch(compare_index, default_label, literals, labels); 825 OpSwitch(compare_index, default_label, literals, labels);
@@ -844,17 +848,21 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
844 ++label_index; 848 ++label_index;
845 } 849 }
846 if (info.stores.ClipDistances()) { 850 if (info.stores.ClipDistances()) {
847 AddLabel(labels[label_index]); 851 if (profile.max_user_clip_distances >= 4) {
848 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)}; 852 AddLabel(labels[label_index]);
849 OpStore(pointer, store_value); 853 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
850 OpReturn(); 854 OpStore(pointer, store_value);
851 ++label_index; 855 OpReturn();
852 AddLabel(labels[label_index]); 856 ++label_index;
853 const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))}; 857 }
854 const Id pointer2{OpAccessChain(output_f32, clip_distances, fixed_index)}; 858 if (profile.max_user_clip_distances >= 8) {
855 OpStore(pointer2, store_value); 859 AddLabel(labels[label_index]);
856 OpReturn(); 860 const Id fixed_index{OpIAdd(U32[1], masked_index, Const(4U))};
857 ++label_index; 861 const Id pointer{OpAccessChain(output_f32, clip_distances, fixed_index)};
862 OpStore(pointer, store_value);
863 OpReturn();
864 ++label_index;
865 }
858 } 866 }
859 AddLabel(end_block); 867 AddLabel(end_block);
860 OpUnreachable(); 868 OpUnreachable();
@@ -1273,7 +1281,9 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1273 throw NotImplementedException("Array of image buffers"); 1281 throw NotImplementedException("Array of image buffers");
1274 } 1282 }
1275 const spv::ImageFormat format{GetImageFormat(desc.format)}; 1283 const spv::ImageFormat format{GetImageFormat(desc.format)};
1276 const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; 1284 const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
1285 const Id image_type{
1286 TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
1277 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1287 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1278 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1288 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1279 Decorate(id, spv::Decoration::Binding, binding); 1289 Decorate(id, spv::Decoration::Binding, binding);
@@ -1283,6 +1293,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1283 .id = id, 1293 .id = id,
1284 .image_type = image_type, 1294 .image_type = image_type,
1285 .count = desc.count, 1295 .count = desc.count,
1296 .is_integer = desc.is_integer,
1286 }); 1297 });
1287 if (profile.supported_spirv >= 0x00010400) { 1298 if (profile.supported_spirv >= 0x00010400) {
1288 interfaces.push_back(id); 1299 interfaces.push_back(id);
@@ -1327,7 +1338,8 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
1327 if (desc.count != 1) { 1338 if (desc.count != 1) {
1328 throw NotImplementedException("Array of images"); 1339 throw NotImplementedException("Array of images");
1329 } 1340 }
1330 const Id image_type{ImageType(*this, desc)}; 1341 const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
1342 const Id image_type{ImageType(*this, desc, sampled_type)};
1331 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1343 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1332 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1344 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1333 Decorate(id, spv::Decoration::Binding, binding); 1345 Decorate(id, spv::Decoration::Binding, binding);
@@ -1337,6 +1349,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
1337 .id = id, 1349 .id = id,
1338 .image_type = image_type, 1350 .image_type = image_type,
1339 .count = desc.count, 1351 .count = desc.count,
1352 .is_integer = desc.is_integer,
1340 }); 1353 });
1341 if (profile.supported_spirv >= 0x00010400) { 1354 if (profile.supported_spirv >= 0x00010400) {
1342 interfaces.push_back(id); 1355 interfaces.push_back(id);
@@ -1528,8 +1541,16 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1528 if (stage == Stage::Fragment) { 1541 if (stage == Stage::Fragment) {
1529 throw NotImplementedException("Storing ClipDistance in fragment stage"); 1542 throw NotImplementedException("Storing ClipDistance in fragment stage");
1530 } 1543 }
1531 const Id type{TypeArray(F32[1], Const(8U))}; 1544 if (profile.max_user_clip_distances > 0) {
1532 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance); 1545 const u32 used{std::min(profile.max_user_clip_distances, 8u)};
1546 const std::array<Id, 8> zero{f32_zero_value, f32_zero_value, f32_zero_value,
1547 f32_zero_value, f32_zero_value, f32_zero_value,
1548 f32_zero_value, f32_zero_value};
1549 const Id type{TypeArray(F32[1], Const(used))};
1550 const Id initializer{ConstantComposite(type, std::span(zero).subspan(0, used))};
1551 clip_distances =
1552 DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance, initializer);
1553 }
1533 } 1554 }
1534 if (info.stores[IR::Attribute::Layer] && 1555 if (info.stores[IR::Attribute::Layer] &&
1535 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1556 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 1aa79863d..56019ad89 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -47,12 +47,14 @@ struct ImageBufferDefinition {
47 Id id; 47 Id id;
48 Id image_type; 48 Id image_type;
49 u32 count; 49 u32 count;
50 bool is_integer;
50}; 51};
51 52
52struct ImageDefinition { 53struct ImageDefinition {
53 Id id; 54 Id id;
54 Id image_type; 55 Id image_type;
55 u32 count; 56 u32 count;
57 bool is_integer;
56}; 58};
57 59
58struct UniformDefinitions { 60struct UniformDefinitions {
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 15285ab0a..e30bf094a 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -24,6 +24,8 @@ public:
24 24
25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; 25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
26 26
27 [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
28
27 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0; 29 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
28 30
29 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 31 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 70292686f..cb82a326c 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
913 } 913 }
914 for (size_t index = 0; index < 8; ++index) { 914 for (size_t index = 0; index < 8; ++index) {
915 const u16 mask{header.vtg.omap_systemc.clip_distances}; 915 const u16 mask{header.vtg.omap_systemc.clip_distances};
916 info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0); 916 const bool used{((mask >> index) & 1) != 0};
917 info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
918 if (used) {
919 info.used_clip_distances = static_cast<u32>(index) + 1;
920 }
917 } 921 }
918 info.stores.Set(IR::Attribute::PrimitiveId, 922 info.stores.Set(IR::Attribute::PrimitiveId,
919 header.vtg.omap_systemb.primitive_array_id != 0); 923 header.vtg.omap_systemb.primitive_array_id != 0);
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index ec12c843a..e4a73a360 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -815,6 +815,15 @@ bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coor
815 return true; 815 return true;
816} 816}
817 817
818void ConvertDerivatives(std::array<IR::Value, 3>& results, IR::IREmitter& ir) {
819 for (size_t i = 0; i < 3; i++) {
820 if (results[i].Type() == IR::Type::U32) {
821 results[i] = results[i].IsImmediate() ? ir.Imm32(Common::BitCast<f32>(results[i].U32()))
822 : ir.BitCast<IR::F32>(IR::U32(results[i]));
823 }
824 }
825}
826
818void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { 827void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
819 IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>(); 828 IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>();
820 auto orig_opcode = inst.GetOpcode(); 829 auto orig_opcode = inst.GetOpcode();
@@ -831,12 +840,14 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
831 if (!offset.IsImmediate()) { 840 if (!offset.IsImmediate()) {
832 return; 841 return;
833 } 842 }
843 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
834 IR::Inst* const inst2 = coords.InstRecursive(); 844 IR::Inst* const inst2 = coords.InstRecursive();
835 std::array<std::array<IR::Value, 3>, 3> results_matrix; 845 std::array<std::array<IR::Value, 3>, 3> results_matrix;
836 for (size_t i = 0; i < 3; i++) { 846 for (size_t i = 0; i < 3; i++) {
837 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) { 847 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
838 return; 848 return;
839 } 849 }
850 ConvertDerivatives(results_matrix[i], ir);
840 } 851 }
841 IR::F32 lod_clamp{}; 852 IR::F32 lod_clamp{};
842 if (info.has_lod_clamp != 0) { 853 if (info.has_lod_clamp != 0) {
@@ -846,7 +857,6 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
846 lod_clamp = IR::F32{bias_lc}; 857 lod_clamp = IR::F32{bias_lc};
847 } 858 }
848 } 859 }
849 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
850 IR::Value new_coords = 860 IR::Value new_coords =
851 ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]); 861 ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]);
852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], 862 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index d374c976a..100437f0e 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -372,6 +372,10 @@ TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAdd
372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf)); 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
373} 373}
374 374
375bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
376 return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
377}
378
375class Descriptors { 379class Descriptors {
376public: 380public:
377 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, 381 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -403,6 +407,7 @@ public:
403 })}; 407 })};
404 image_buffer_descriptors[index].is_written |= desc.is_written; 408 image_buffer_descriptors[index].is_written |= desc.is_written;
405 image_buffer_descriptors[index].is_read |= desc.is_read; 409 image_buffer_descriptors[index].is_read |= desc.is_read;
410 image_buffer_descriptors[index].is_integer |= desc.is_integer;
406 return index; 411 return index;
407 } 412 }
408 413
@@ -432,6 +437,7 @@ public:
432 })}; 437 })};
433 image_descriptors[index].is_written |= desc.is_written; 438 image_descriptors[index].is_written |= desc.is_written;
434 image_descriptors[index].is_read |= desc.is_read; 439 image_descriptors[index].is_read |= desc.is_read;
440 image_descriptors[index].is_integer |= desc.is_integer;
435 return index; 441 return index;
436 } 442 }
437 443
@@ -469,6 +475,20 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
469 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); 475 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
470} 476}
471 477
478bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
479 switch (pixel_format) {
480 case TexturePixelFormat::A8B8G8R8_SNORM:
481 case TexturePixelFormat::R8G8_SNORM:
482 case TexturePixelFormat::R8_SNORM:
483 case TexturePixelFormat::R16G16B16A16_SNORM:
484 case TexturePixelFormat::R16G16_SNORM:
485 case TexturePixelFormat::R16_SNORM:
486 return true;
487 default:
488 return false;
489 }
490}
491
472void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { 492void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
473 const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; 493 const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
474 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 494 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
@@ -587,11 +607,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
587 } 607 }
588 const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead}; 608 const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
589 const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite}; 609 const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
610 const bool is_integer{IsTexturePixelFormatInteger(env, cbuf)};
590 if (flags.type == TextureType::Buffer) { 611 if (flags.type == TextureType::Buffer) {
591 index = descriptors.Add(ImageBufferDescriptor{ 612 index = descriptors.Add(ImageBufferDescriptor{
592 .format = flags.image_format, 613 .format = flags.image_format,
593 .is_written = is_written, 614 .is_written = is_written,
594 .is_read = is_read, 615 .is_read = is_read,
616 .is_integer = is_integer,
595 .cbuf_index = cbuf.index, 617 .cbuf_index = cbuf.index,
596 .cbuf_offset = cbuf.offset, 618 .cbuf_offset = cbuf.offset,
597 .count = cbuf.count, 619 .count = cbuf.count,
@@ -603,6 +625,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
603 .format = flags.image_format, 625 .format = flags.image_format,
604 .is_written = is_written, 626 .is_written = is_written,
605 .is_read = is_read, 627 .is_read = is_read,
628 .is_integer = is_integer,
606 .cbuf_index = cbuf.index, 629 .cbuf_index = cbuf.index,
607 .cbuf_offset = cbuf.offset, 630 .cbuf_offset = cbuf.offset,
608 .count = cbuf.count, 631 .count = cbuf.count,
@@ -658,7 +681,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
658 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch && 681 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
659 flags.type == TextureType::Buffer) { 682 flags.type == TextureType::Buffer) {
660 const auto pixel_format = ReadTexturePixelFormat(env, cbuf); 683 const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
661 if (pixel_format != TexturePixelFormat::OTHER) { 684 if (IsPixelFormatSNorm(pixel_format)) {
662 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); 685 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
663 } 686 }
664 } 687 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 66901a965..7578d41cc 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -87,6 +87,8 @@ struct Profile {
87 bool has_broken_robust{}; 87 bool has_broken_robust{};
88 88
89 u64 min_ssbo_alignment{}; 89 u64 min_ssbo_alignment{};
90
91 u32 max_user_clip_distances{};
90}; 92};
91 93
92} // namespace Shader 94} // namespace Shader
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index b4b4afd37..ed13e6820 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -35,14 +35,109 @@ enum class TextureType : u32 {
35}; 35};
36constexpr u32 NUM_TEXTURE_TYPES = 9; 36constexpr u32 NUM_TEXTURE_TYPES = 9;
37 37
38enum class TexturePixelFormat : u32 { 38enum class TexturePixelFormat {
39 A8B8G8R8_UNORM,
39 A8B8G8R8_SNORM, 40 A8B8G8R8_SNORM,
41 A8B8G8R8_SINT,
42 A8B8G8R8_UINT,
43 R5G6B5_UNORM,
44 B5G6R5_UNORM,
45 A1R5G5B5_UNORM,
46 A2B10G10R10_UNORM,
47 A2B10G10R10_UINT,
48 A2R10G10B10_UNORM,
49 A1B5G5R5_UNORM,
50 A5B5G5R1_UNORM,
51 R8_UNORM,
40 R8_SNORM, 52 R8_SNORM,
41 R8G8_SNORM, 53 R8_SINT,
54 R8_UINT,
55 R16G16B16A16_FLOAT,
56 R16G16B16A16_UNORM,
42 R16G16B16A16_SNORM, 57 R16G16B16A16_SNORM,
43 R16G16_SNORM, 58 R16G16B16A16_SINT,
59 R16G16B16A16_UINT,
60 B10G11R11_FLOAT,
61 R32G32B32A32_UINT,
62 BC1_RGBA_UNORM,
63 BC2_UNORM,
64 BC3_UNORM,
65 BC4_UNORM,
66 BC4_SNORM,
67 BC5_UNORM,
68 BC5_SNORM,
69 BC7_UNORM,
70 BC6H_UFLOAT,
71 BC6H_SFLOAT,
72 ASTC_2D_4X4_UNORM,
73 B8G8R8A8_UNORM,
74 R32G32B32A32_FLOAT,
75 R32G32B32A32_SINT,
76 R32G32_FLOAT,
77 R32G32_SINT,
78 R32_FLOAT,
79 R16_FLOAT,
80 R16_UNORM,
44 R16_SNORM, 81 R16_SNORM,
45 OTHER 82 R16_UINT,
83 R16_SINT,
84 R16G16_UNORM,
85 R16G16_FLOAT,
86 R16G16_UINT,
87 R16G16_SINT,
88 R16G16_SNORM,
89 R32G32B32_FLOAT,
90 A8B8G8R8_SRGB,
91 R8G8_UNORM,
92 R8G8_SNORM,
93 R8G8_SINT,
94 R8G8_UINT,
95 R32G32_UINT,
96 R16G16B16X16_FLOAT,
97 R32_UINT,
98 R32_SINT,
99 ASTC_2D_8X8_UNORM,
100 ASTC_2D_8X5_UNORM,
101 ASTC_2D_5X4_UNORM,
102 B8G8R8A8_SRGB,
103 BC1_RGBA_SRGB,
104 BC2_SRGB,
105 BC3_SRGB,
106 BC7_SRGB,
107 A4B4G4R4_UNORM,
108 G4R4_UNORM,
109 ASTC_2D_4X4_SRGB,
110 ASTC_2D_8X8_SRGB,
111 ASTC_2D_8X5_SRGB,
112 ASTC_2D_5X4_SRGB,
113 ASTC_2D_5X5_UNORM,
114 ASTC_2D_5X5_SRGB,
115 ASTC_2D_10X8_UNORM,
116 ASTC_2D_10X8_SRGB,
117 ASTC_2D_6X6_UNORM,
118 ASTC_2D_6X6_SRGB,
119 ASTC_2D_10X6_UNORM,
120 ASTC_2D_10X6_SRGB,
121 ASTC_2D_10X5_UNORM,
122 ASTC_2D_10X5_SRGB,
123 ASTC_2D_10X10_UNORM,
124 ASTC_2D_10X10_SRGB,
125 ASTC_2D_12X10_UNORM,
126 ASTC_2D_12X10_SRGB,
127 ASTC_2D_12X12_UNORM,
128 ASTC_2D_12X12_SRGB,
129 ASTC_2D_8X6_UNORM,
130 ASTC_2D_8X6_SRGB,
131 ASTC_2D_6X5_UNORM,
132 ASTC_2D_6X5_SRGB,
133 E5B9G9R9_FLOAT,
134 D32_FLOAT,
135 D16_UNORM,
136 X8_D24_UNORM,
137 S8_UINT,
138 D24_UNORM_S8_UINT,
139 S8_UINT_D24_UNORM,
140 D32_FLOAT_S8_UINT,
46}; 141};
47 142
48enum class ImageFormat : u32 { 143enum class ImageFormat : u32 {
@@ -97,6 +192,7 @@ struct ImageBufferDescriptor {
97 ImageFormat format; 192 ImageFormat format;
98 bool is_written; 193 bool is_written;
99 bool is_read; 194 bool is_read;
195 bool is_integer;
100 u32 cbuf_index; 196 u32 cbuf_index;
101 u32 cbuf_offset; 197 u32 cbuf_offset;
102 u32 count; 198 u32 count;
@@ -129,6 +225,7 @@ struct ImageDescriptor {
129 ImageFormat format; 225 ImageFormat format;
130 bool is_written; 226 bool is_written;
131 bool is_read; 227 bool is_read;
228 bool is_integer;
132 u32 cbuf_index; 229 u32 cbuf_index;
133 u32 cbuf_offset; 230 u32 cbuf_offset;
134 u32 count; 231 u32 count;
@@ -227,6 +324,8 @@ struct Info {
227 bool requires_layer_emulation{}; 324 bool requires_layer_emulation{};
228 IR::Attribute emulated_layer{}; 325 IR::Attribute emulated_layer{};
229 326
327 u32 used_clip_distances{};
328
230 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 329 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
231 constant_buffer_descriptors; 330 constant_buffer_descriptors;
232 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; 331 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index 1a28e862b..cb040c942 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -12,6 +12,7 @@ using namespace Common::Literals;
12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39; 12static constexpr size_t VIRTUAL_SIZE = 1ULL << 39;
13static constexpr size_t BACKING_SIZE = 4_GiB; 13static constexpr size_t BACKING_SIZE = 4_GiB;
14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite; 14static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
15static constexpr auto HEAP = false;
15 16
16TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { 17TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
17 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); } 18 { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
@@ -20,7 +21,7 @@ TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
20 21
21TEST_CASE("HostMemory: Simple map", "[common]") { 22TEST_CASE("HostMemory: Simple map", "[common]") {
22 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 23 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
23 mem.Map(0x5000, 0x8000, 0x1000, PERMS); 24 mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
24 25
25 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 26 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
26 data[0] = 50; 27 data[0] = 50;
@@ -29,8 +30,8 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
29 30
30TEST_CASE("HostMemory: Simple mirror map", "[common]") { 31TEST_CASE("HostMemory: Simple mirror map", "[common]") {
31 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 32 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
32 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 33 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
33 mem.Map(0x8000, 0x4000, 0x1000, PERMS); 34 mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
34 35
35 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000; 36 volatile u8* const mirror_a = mem.VirtualBasePointer() + 0x5000;
36 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000; 37 volatile u8* const mirror_b = mem.VirtualBasePointer() + 0x8000;
@@ -40,116 +41,116 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
40 41
41TEST_CASE("HostMemory: Simple unmap", "[common]") { 42TEST_CASE("HostMemory: Simple unmap", "[common]") {
42 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 43 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
43 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 44 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
44 45
45 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 46 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
46 data[75] = 50; 47 data[75] = 50;
47 REQUIRE(data[75] == 50); 48 REQUIRE(data[75] == 50);
48 49
49 mem.Unmap(0x5000, 0x2000); 50 mem.Unmap(0x5000, 0x2000, HEAP);
50} 51}
51 52
52TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { 53TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
53 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 54 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
54 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 55 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
55 56
56 volatile u8* const data = mem.VirtualBasePointer() + 0x5000; 57 volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
57 data[0] = 50; 58 data[0] = 50;
58 REQUIRE(data[0] == 50); 59 REQUIRE(data[0] == 50);
59 60
60 mem.Unmap(0x5000, 0x2000); 61 mem.Unmap(0x5000, 0x2000, HEAP);
61 62
62 mem.Map(0x5000, 0x3000, 0x2000, PERMS); 63 mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
63 REQUIRE(data[0] == 50); 64 REQUIRE(data[0] == 50);
64 65
65 mem.Map(0x7000, 0x2000, 0x5000, PERMS); 66 mem.Map(0x7000, 0x2000, 0x5000, PERMS, HEAP);
66 REQUIRE(data[0x3000] == 50); 67 REQUIRE(data[0x3000] == 50);
67} 68}
68 69
69TEST_CASE("HostMemory: Nieche allocation", "[common]") { 70TEST_CASE("HostMemory: Nieche allocation", "[common]") {
70 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 71 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
71 mem.Map(0x0000, 0, 0x20000, PERMS); 72 mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
72 mem.Unmap(0x0000, 0x4000); 73 mem.Unmap(0x0000, 0x4000, HEAP);
73 mem.Map(0x1000, 0, 0x2000, PERMS); 74 mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
74 mem.Map(0x3000, 0, 0x1000, PERMS); 75 mem.Map(0x3000, 0, 0x1000, PERMS, HEAP);
75 mem.Map(0, 0, 0x1000, PERMS); 76 mem.Map(0, 0, 0x1000, PERMS, HEAP);
76} 77}
77 78
78TEST_CASE("HostMemory: Full unmap", "[common]") { 79TEST_CASE("HostMemory: Full unmap", "[common]") {
79 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 80 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
80 mem.Map(0x8000, 0, 0x4000, PERMS); 81 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
81 mem.Unmap(0x8000, 0x4000); 82 mem.Unmap(0x8000, 0x4000, HEAP);
82 mem.Map(0x6000, 0, 0x16000, PERMS); 83 mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
83} 84}
84 85
85TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { 86TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
86 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 87 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
87 mem.Map(0x0000, 0, 0x4000, PERMS); 88 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
88 mem.Unmap(0x2000, 0x4000); 89 mem.Unmap(0x2000, 0x4000, HEAP);
89 mem.Map(0x2000, 0x80000, 0x4000, PERMS); 90 mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
90} 91}
91 92
92TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { 93TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
93 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 94 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
94 mem.Map(0x8000, 0, 0x4000, PERMS); 95 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
95 mem.Unmap(0x6000, 0x4000); 96 mem.Unmap(0x6000, 0x4000, HEAP);
96 mem.Map(0x8000, 0, 0x2000, PERMS); 97 mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
97} 98}
98 99
99TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { 100TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
100 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 101 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
101 mem.Map(0x0000, 0, 0x4000, PERMS); 102 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
102 mem.Map(0x4000, 0, 0x1b000, PERMS); 103 mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
103 mem.Unmap(0x3000, 0x1c000); 104 mem.Unmap(0x3000, 0x1c000, HEAP);
104 mem.Map(0x3000, 0, 0x20000, PERMS); 105 mem.Map(0x3000, 0, 0x20000, PERMS, HEAP);
105} 106}
106 107
107TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { 108TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
108 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 109 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
109 mem.Map(0x0000, 0, 0x4000, PERMS); 110 mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
110 mem.Map(0x4000, 0, 0x4000, PERMS); 111 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
111 mem.Unmap(0x2000, 0x4000); 112 mem.Unmap(0x2000, 0x4000, HEAP);
112 mem.Map(0x2000, 0, 0x4000, PERMS); 113 mem.Map(0x2000, 0, 0x4000, PERMS, HEAP);
113} 114}
114 115
115TEST_CASE("HostMemory: Unmap to origin", "[common]") { 116TEST_CASE("HostMemory: Unmap to origin", "[common]") {
116 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 117 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
117 mem.Map(0x4000, 0, 0x4000, PERMS); 118 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
118 mem.Map(0x8000, 0, 0x4000, PERMS); 119 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
119 mem.Unmap(0x4000, 0x4000); 120 mem.Unmap(0x4000, 0x4000, HEAP);
120 mem.Map(0, 0, 0x4000, PERMS); 121 mem.Map(0, 0, 0x4000, PERMS, HEAP);
121 mem.Map(0x4000, 0, 0x4000, PERMS); 122 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
122} 123}
123 124
124TEST_CASE("HostMemory: Unmap to right", "[common]") { 125TEST_CASE("HostMemory: Unmap to right", "[common]") {
125 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 126 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
126 mem.Map(0x4000, 0, 0x4000, PERMS); 127 mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
127 mem.Map(0x8000, 0, 0x4000, PERMS); 128 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
128 mem.Unmap(0x8000, 0x4000); 129 mem.Unmap(0x8000, 0x4000, HEAP);
129 mem.Map(0x8000, 0, 0x4000, PERMS); 130 mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
130} 131}
131 132
132TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { 133TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
133 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 134 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
134 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 135 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
135 136
136 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 137 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
137 ptr[0x1000] = 17; 138 ptr[0x1000] = 17;
138 139
139 mem.Unmap(0x6000, 0x2000); 140 mem.Unmap(0x6000, 0x2000, HEAP);
140 141
141 REQUIRE(ptr[0x1000] == 17); 142 REQUIRE(ptr[0x1000] == 17);
142} 143}
143 144
144TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { 145TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
145 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 146 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
146 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 147 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
147 148
148 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 149 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
149 ptr[0x3000] = 19; 150 ptr[0x3000] = 19;
150 ptr[0x3fff] = 12; 151 ptr[0x3fff] = 12;
151 152
152 mem.Unmap(0x4000, 0x2000); 153 mem.Unmap(0x4000, 0x2000, HEAP);
153 154
154 REQUIRE(ptr[0x3000] == 19); 155 REQUIRE(ptr[0x3000] == 19);
155 REQUIRE(ptr[0x3fff] == 12); 156 REQUIRE(ptr[0x3fff] == 12);
@@ -157,13 +158,13 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
157 158
158TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { 159TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
159 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 160 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
160 mem.Map(0x4000, 0x10000, 0x4000, PERMS); 161 mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
161 162
162 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 163 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
163 ptr[0x0000] = 19; 164 ptr[0x0000] = 19;
164 ptr[0x3fff] = 12; 165 ptr[0x3fff] = 12;
165 166
166 mem.Unmap(0x1000, 0x2000); 167 mem.Unmap(0x1000, 0x2000, HEAP);
167 168
168 REQUIRE(ptr[0x0000] == 19); 169 REQUIRE(ptr[0x0000] == 19);
169 REQUIRE(ptr[0x3fff] == 12); 170 REQUIRE(ptr[0x3fff] == 12);
@@ -171,14 +172,14 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
171 172
172TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { 173TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
173 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); 174 HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
174 mem.Map(0x4000, 0x10000, 0x2000, PERMS); 175 mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
175 mem.Map(0x6000, 0x20000, 0x2000, PERMS); 176 mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
176 177
177 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; 178 volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
178 ptr[0x0000] = 19; 179 ptr[0x0000] = 19;
179 ptr[0x3fff] = 12; 180 ptr[0x3fff] = 12;
180 181
181 mem.Unmap(0x5000, 0x2000); 182 mem.Unmap(0x5000, 0x2000, HEAP);
182 183
183 REQUIRE(ptr[0x0000] == 19); 184 REQUIRE(ptr[0x0000] == 19);
184 REQUIRE(ptr[0x3fff] == 12); 185 REQUIRE(ptr[0x3fff] == 12);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index f08afbf9a..81898a1d3 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -16,20 +16,16 @@
16 16
17namespace { 17namespace {
18// Numbers are chosen randomly to make sure the correct one is given. 18// Numbers are chosen randomly to make sure the correct one is given.
19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
20constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}}; 19constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
21std::array<s64, 5> delays{}; 20std::array<s64, 5> delays{};
22 21std::bitset<5> callbacks_ran_flags;
23std::bitset<CB_IDS.size()> callbacks_ran_flags;
24u64 expected_callback = 0; 22u64 expected_callback = 0;
25 23
26template <unsigned int IDX> 24template <unsigned int IDX>
27std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time, 25std::optional<std::chrono::nanoseconds> HostCallbackTemplate(s64 time,
28 std::chrono::nanoseconds ns_late) { 26 std::chrono::nanoseconds ns_late) {
29 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 27 static_assert(IDX < callbacks_ran_flags.size(), "IDX out of range");
30 callbacks_ran_flags.set(IDX); 28 callbacks_ran_flags.set(IDX);
31 REQUIRE(CB_IDS[IDX] == user_data);
32 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
33 delays[IDX] = ns_late.count(); 29 delays[IDX] = ns_late.count();
34 ++expected_callback; 30 ++expected_callback;
35 return std::nullopt; 31 return std::nullopt;
@@ -76,7 +72,7 @@ TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
76 const u64 order = calls_order[i]; 72 const u64 order = calls_order[i];
77 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; 73 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
78 74
79 core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); 75 core_timing.ScheduleEvent(future_ns, events[order]);
80 } 76 }
81 /// test pause 77 /// test pause
82 REQUIRE(callbacks_ran_flags.none()); 78 REQUIRE(callbacks_ran_flags.none());
@@ -118,7 +114,7 @@ TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
118 for (std::size_t i = 0; i < events.size(); i++) { 114 for (std::size_t i = 0; i < events.size(); i++) {
119 const u64 order = calls_order[i]; 115 const u64 order = calls_order[i];
120 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)}; 116 const auto future_ns = std::chrono::nanoseconds{static_cast<s64>(i * one_micro + 100)};
121 core_timing.ScheduleEvent(future_ns, events[order], CB_IDS[order]); 117 core_timing.ScheduleEvent(future_ns, events[order]);
122 } 118 }
123 119
124 const u64 end = core_timing.GetGlobalTimeNs().count(); 120 const u64 end = core_timing.GetGlobalTimeNs().count();
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 592c28ba3..95ba4f76c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -586,14 +586,22 @@ void Maxwell3D::ProcessQueryCondition() {
586} 586}
587 587
588void Maxwell3D::ProcessCounterReset() { 588void Maxwell3D::ProcessCounterReset() {
589 switch (regs.clear_report_value) { 589 const auto query_type = [clear_report = regs.clear_report_value]() {
590 case Regs::ClearReport::ZPassPixelCount: 590 switch (clear_report) {
591 rasterizer->ResetCounter(VideoCommon::QueryType::ZPassPixelCount64); 591 case Tegra::Engines::Maxwell3D::Regs::ClearReport::ZPassPixelCount:
592 break; 592 return VideoCommon::QueryType::ZPassPixelCount64;
593 default: 593 case Tegra::Engines::Maxwell3D::Regs::ClearReport::StreamingPrimitivesSucceeded:
594 LOG_DEBUG(Render_OpenGL, "Unimplemented counter reset={}", regs.clear_report_value); 594 return VideoCommon::QueryType::StreamingPrimitivesSucceeded;
595 break; 595 case Tegra::Engines::Maxwell3D::Regs::ClearReport::PrimitivesGenerated:
596 } 596 return VideoCommon::QueryType::PrimitivesGenerated;
597 case Tegra::Engines::Maxwell3D::Regs::ClearReport::VtgPrimitivesOut:
598 return VideoCommon::QueryType::VtgPrimitivesOut;
599 default:
600 LOG_DEBUG(HW_GPU, "Unimplemented counter reset={}", clear_report);
601 return VideoCommon::QueryType::Payload;
602 }
603 }();
604 rasterizer->ResetCounter(query_type);
597} 605}
598 606
599void Maxwell3D::ProcessSyncPoint() { 607void Maxwell3D::ProcessSyncPoint() {
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 422d4d859..56fbff306 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -228,7 +228,7 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
228 228
229 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( 229 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
230 memory_manager, src_operand.address, src_size, &read_buffer); 230 memory_manager, src_operand.address, src_size, &read_buffer);
231 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> 231 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
232 tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer); 232 tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer);
233 233
234 UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth, 234 UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth,
@@ -292,7 +292,7 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
292 GPUVAddr dst_addr = regs.offset_out; 292 GPUVAddr dst_addr = regs.offset_out;
293 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( 293 Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer(
294 memory_manager, src_addr, src_size, &read_buffer); 294 memory_manager, src_addr, src_size, &read_buffer);
295 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> 295 Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadCachedWrite>
296 tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer); 296 tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer);
297 297
298 // If the input is linear and the output is tiled, swizzle the input and copy it over. 298 // If the input is linear and the output is tiled, swizzle the input and copy it over.
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index 046c8085e..46e853e04 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -327,12 +327,13 @@ public:
327 explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {} 327 explicit HLE_DrawIndirectByteCount(Maxwell3D& maxwell3d_) : HLEMacroImpl(maxwell3d_) {}
328 328
329 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override { 329 void Execute(const std::vector<u32>& parameters, [[maybe_unused]] u32 method) override {
330 const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback();
331
330 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU); 332 auto topology = static_cast<Maxwell3D::Regs::PrimitiveTopology>(parameters[0] & 0xFFFFU);
331 if (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology)) { 333 if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) {
332 Fallback(parameters); 334 Fallback(parameters);
333 return; 335 return;
334 } 336 }
335
336 auto& params = maxwell3d.draw_manager->GetIndirectParams(); 337 auto& params = maxwell3d.draw_manager->GetIndirectParams();
337 params.is_byte_count = true; 338 params.is_byte_count = true;
338 params.is_indexed = false; 339 params.is_indexed = false;
@@ -503,6 +504,8 @@ public:
503 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true); 504 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(launch_dma)), 0x1011, true);
504 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)), 505 maxwell3d.CallMethod(static_cast<size_t>(MAXWELL3D_REG_INDEX(inline_data)),
505 regs.transform_feedback.controls[0].stride, true); 506 regs.transform_feedback.controls[0].stride, true);
507
508 maxwell3d.Rasterizer().RegisterTransformFeedback(regs.upload.dest.Address());
506 } 509 }
507}; 510};
508 511
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 9fcaeeac7..a64404ce4 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -28,8 +28,11 @@
28namespace VideoCore { 28namespace VideoCore {
29enum class QueryType { 29enum class QueryType {
30 SamplesPassed, 30 SamplesPassed,
31 PrimitivesGenerated,
32 TfbPrimitivesWritten,
33 Count,
31}; 34};
32constexpr std::size_t NumQueryTypes = 1; 35constexpr std::size_t NumQueryTypes = static_cast<size_t>(QueryType::Count);
33} // namespace VideoCore 36} // namespace VideoCore
34 37
35namespace VideoCommon { 38namespace VideoCommon {
@@ -44,15 +47,6 @@ public:
44 explicit CounterStreamBase(QueryCache& cache_, VideoCore::QueryType type_) 47 explicit CounterStreamBase(QueryCache& cache_, VideoCore::QueryType type_)
45 : cache{cache_}, type{type_} {} 48 : cache{cache_}, type{type_} {}
46 49
47 /// Updates the state of the stream, enabling or disabling as needed.
48 void Update(bool enabled) {
49 if (enabled) {
50 Enable();
51 } else {
52 Disable();
53 }
54 }
55
56 /// Resets the stream to zero. It doesn't disable the query after resetting. 50 /// Resets the stream to zero. It doesn't disable the query after resetting.
57 void Reset() { 51 void Reset() {
58 if (current) { 52 if (current) {
@@ -80,7 +74,6 @@ public:
80 return current != nullptr; 74 return current != nullptr;
81 } 75 }
82 76
83private:
84 /// Enables the stream. 77 /// Enables the stream.
85 void Enable() { 78 void Enable() {
86 if (current) { 79 if (current) {
@@ -97,6 +90,7 @@ private:
97 last = std::exchange(current, nullptr); 90 last = std::exchange(current, nullptr);
98 } 91 }
99 92
93private:
100 QueryCache& cache; 94 QueryCache& cache;
101 const VideoCore::QueryType type; 95 const VideoCore::QueryType type;
102 96
@@ -112,8 +106,14 @@ public:
112 : rasterizer{rasterizer_}, 106 : rasterizer{rasterizer_},
113 // Use reinterpret_cast instead of static_cast as workaround for 107 // Use reinterpret_cast instead of static_cast as workaround for
114 // UBSan bug (https://github.com/llvm/llvm-project/issues/59060) 108 // UBSan bug (https://github.com/llvm/llvm-project/issues/59060)
115 cpu_memory{cpu_memory_}, streams{{CounterStream{reinterpret_cast<QueryCache&>(*this), 109 cpu_memory{cpu_memory_}, streams{{
116 VideoCore::QueryType::SamplesPassed}}} { 110 {CounterStream{reinterpret_cast<QueryCache&>(*this),
111 VideoCore::QueryType::SamplesPassed}},
112 {CounterStream{reinterpret_cast<QueryCache&>(*this),
113 VideoCore::QueryType::PrimitivesGenerated}},
114 {CounterStream{reinterpret_cast<QueryCache&>(*this),
115 VideoCore::QueryType::TfbPrimitivesWritten}},
116 }} {
117 (void)slot_async_jobs.insert(); // Null value 117 (void)slot_async_jobs.insert(); // Null value
118 } 118 }
119 119
@@ -157,12 +157,11 @@ public:
157 AsyncFlushQuery(query, timestamp, lock); 157 AsyncFlushQuery(query, timestamp, lock);
158 } 158 }
159 159
160 /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. 160 /// Enables all available GPU counters
161 void UpdateCounters() { 161 void EnableCounters() {
162 std::unique_lock lock{mutex}; 162 std::unique_lock lock{mutex};
163 if (maxwell3d) { 163 for (auto& stream : streams) {
164 const auto& regs = maxwell3d->regs; 164 stream.Enable();
165 Stream(VideoCore::QueryType::SamplesPassed).Update(regs.zpass_pixel_count_enable);
166 } 165 }
167 } 166 }
168 167
@@ -176,7 +175,7 @@ public:
176 void DisableStreams() { 175 void DisableStreams() {
177 std::unique_lock lock{mutex}; 176 std::unique_lock lock{mutex};
178 for (auto& stream : streams) { 177 for (auto& stream : streams) {
179 stream.Update(false); 178 stream.Disable();
180 } 179 }
181 } 180 }
182 181
@@ -353,7 +352,7 @@ private:
353 352
354 std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{}; 353 std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{};
355 std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes; 354 std::list<std::shared_ptr<std::vector<AsyncJobId>>> committed_flushes;
356}; 355}; // namespace VideoCommon
357 356
358template <class QueryCache, class HostCounter> 357template <class QueryCache, class HostCounter>
359class HostCounterBase { 358class HostCounterBase {
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index af1469147..49224ca85 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -173,5 +173,13 @@ public:
173 virtual void BindChannel(Tegra::Control::ChannelState& channel) {} 173 virtual void BindChannel(Tegra::Control::ChannelState& channel) {}
174 174
175 virtual void ReleaseChannel(s32 channel_id) {} 175 virtual void ReleaseChannel(s32 channel_id) {}
176
177 /// Register the address as a Transform Feedback Object
178 virtual void RegisterTransformFeedback(GPUVAddr tfb_object_addr) {}
179
180 /// Returns true when the rasterizer has Draw Transform Feedback capabilities
181 virtual bool HasDrawTransformFeedback() {
182 return false;
183 }
176}; 184};
177} // namespace VideoCore 185} // namespace VideoCore
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index b787b6994..517ac14dd 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -376,4 +376,15 @@ void BufferCacheRuntime::BindImageBuffer(Buffer& buffer, u32 offset, u32 size, P
376 *image_handles++ = buffer.View(offset, size, format); 376 *image_handles++ = buffer.View(offset, size, format);
377} 377}
378 378
379void BufferCacheRuntime::BindTransformFeedbackObject(GPUVAddr tfb_object_addr) {
380 OGLTransformFeedback& tfb_object = tfb_objects[tfb_object_addr];
381 tfb_object.Create();
382 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfb_object.handle);
383}
384
385GLuint BufferCacheRuntime::GetTransformFeedbackObject(GPUVAddr tfb_object_addr) {
386 ASSERT(tfb_objects.contains(tfb_object_addr));
387 return tfb_objects[tfb_object_addr].handle;
388}
389
379} // namespace OpenGL 390} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 1e8708f59..2c18de166 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8#include <unordered_map>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "video_core/buffer_cache/buffer_cache_base.h" 11#include "video_core/buffer_cache/buffer_cache_base.h"
@@ -121,6 +122,9 @@ public:
121 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size, 122 void BindImageBuffer(Buffer& buffer, u32 offset, u32 size,
122 VideoCore::Surface::PixelFormat format); 123 VideoCore::Surface::PixelFormat format);
123 124
125 void BindTransformFeedbackObject(GPUVAddr tfb_object_addr);
126 GLuint GetTransformFeedbackObject(GPUVAddr tfb_object_addr);
127
124 u64 GetDeviceMemoryUsage() const; 128 u64 GetDeviceMemoryUsage() const;
125 129
126 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) { 130 void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
@@ -233,6 +237,7 @@ private:
233 u32 index_buffer_offset = 0; 237 u32 index_buffer_offset = 0;
234 238
235 u64 device_access_memory; 239 u64 device_access_memory;
240 std::unordered_map<GPUVAddr, OGLTransformFeedback> tfb_objects;
236}; 241};
237 242
238struct BufferCacheParams { 243struct BufferCacheParams {
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index ec142d48e..fef7360ed 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -18,16 +18,27 @@ namespace OpenGL {
18 18
19namespace { 19namespace {
20 20
21constexpr std::array<GLenum, VideoCore::NumQueryTypes> QueryTargets = {GL_SAMPLES_PASSED};
22
23constexpr GLenum GetTarget(VideoCore::QueryType type) { 21constexpr GLenum GetTarget(VideoCore::QueryType type) {
24 return QueryTargets[static_cast<std::size_t>(type)]; 22 switch (type) {
23 case VideoCore::QueryType::SamplesPassed:
24 return GL_SAMPLES_PASSED;
25 case VideoCore::QueryType::PrimitivesGenerated:
26 return GL_PRIMITIVES_GENERATED;
27 case VideoCore::QueryType::TfbPrimitivesWritten:
28 return GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;
29 default:
30 break;
31 }
32 UNIMPLEMENTED_MSG("Query type {}", type);
33 return 0;
25} 34}
26 35
27} // Anonymous namespace 36} // Anonymous namespace
28 37
29QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_) 38QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
30 : QueryCacheLegacy(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {} 39 : QueryCacheLegacy(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {
40 EnableCounters();
41}
31 42
32QueryCache::~QueryCache() = default; 43QueryCache::~QueryCache() = default;
33 44
@@ -103,13 +114,13 @@ u64 CachedQuery::Flush([[maybe_unused]] bool async) {
103 auto& stream = cache->Stream(type); 114 auto& stream = cache->Stream(type);
104 const bool slice_counter = WaitPending() && stream.IsEnabled(); 115 const bool slice_counter = WaitPending() && stream.IsEnabled();
105 if (slice_counter) { 116 if (slice_counter) {
106 stream.Update(false); 117 stream.Disable();
107 } 118 }
108 119
109 auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush(); 120 auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush();
110 121
111 if (slice_counter) { 122 if (slice_counter) {
112 stream.Update(true); 123 stream.Enable();
113 } 124 }
114 125
115 return result; 126 return result;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4832c03c5..7a5fad735 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -51,6 +51,22 @@ constexpr size_t NUM_SUPPORTED_VERTEX_ATTRIBUTES = 16;
51void oglEnable(GLenum cap, bool state) { 51void oglEnable(GLenum cap, bool state) {
52 (state ? glEnable : glDisable)(cap); 52 (state ? glEnable : glDisable)(cap);
53} 53}
54
55std::optional<VideoCore::QueryType> MaxwellToVideoCoreQuery(VideoCommon::QueryType type) {
56 switch (type) {
57 case VideoCommon::QueryType::PrimitivesGenerated:
58 case VideoCommon::QueryType::VtgPrimitivesOut:
59 return VideoCore::QueryType::PrimitivesGenerated;
60 case VideoCommon::QueryType::ZPassPixelCount64:
61 return VideoCore::QueryType::SamplesPassed;
62 case VideoCommon::QueryType::StreamingPrimitivesSucceeded:
63 // case VideoCommon::QueryType::StreamingByteCount:
64 // TODO: StreamingByteCount = StreamingPrimitivesSucceeded * num_verts * vert_stride
65 return VideoCore::QueryType::TfbPrimitivesWritten;
66 default:
67 return std::nullopt;
68 }
69}
54} // Anonymous namespace 70} // Anonymous namespace
55 71
56RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, 72RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
@@ -216,7 +232,6 @@ void RasterizerOpenGL::PrepareDraw(bool is_indexed, Func&& draw_func) {
216 232
217 SCOPE_EXIT({ gpu.TickWork(); }); 233 SCOPE_EXIT({ gpu.TickWork(); });
218 gpu_memory->FlushCaching(); 234 gpu_memory->FlushCaching();
219 query_cache.UpdateCounters();
220 235
221 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; 236 GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
222 if (!pipeline) { 237 if (!pipeline) {
@@ -294,6 +309,13 @@ void RasterizerOpenGL::DrawIndirect() {
294 const auto& params = maxwell3d->draw_manager->GetIndirectParams(); 309 const auto& params = maxwell3d->draw_manager->GetIndirectParams();
295 buffer_cache.SetDrawIndirect(&params); 310 buffer_cache.SetDrawIndirect(&params);
296 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) { 311 PrepareDraw(params.is_indexed, [this, &params](GLenum primitive_mode) {
312 if (params.is_byte_count) {
313 const GPUVAddr tfb_object_base_addr = params.indirect_start_address - 4U;
314 const GLuint tfb_object =
315 buffer_cache_runtime.GetTransformFeedbackObject(tfb_object_base_addr);
316 glDrawTransformFeedback(primitive_mode, tfb_object);
317 return;
318 }
297 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer(); 319 const auto [buffer, offset] = buffer_cache.GetDrawIndirectBuffer();
298 const GLvoid* const gl_offset = 320 const GLvoid* const gl_offset =
299 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset)); 321 reinterpret_cast<const GLvoid*>(static_cast<uintptr_t>(offset));
@@ -334,7 +356,6 @@ void RasterizerOpenGL::DrawTexture() {
334 MICROPROFILE_SCOPE(OpenGL_Drawing); 356 MICROPROFILE_SCOPE(OpenGL_Drawing);
335 357
336 SCOPE_EXIT({ gpu.TickWork(); }); 358 SCOPE_EXIT({ gpu.TickWork(); });
337 query_cache.UpdateCounters();
338 359
339 texture_cache.SynchronizeGraphicsDescriptors(); 360 texture_cache.SynchronizeGraphicsDescriptors();
340 texture_cache.UpdateRenderTargets(false); 361 texture_cache.UpdateRenderTargets(false);
@@ -401,21 +422,28 @@ void RasterizerOpenGL::DispatchCompute() {
401} 422}
402 423
403void RasterizerOpenGL::ResetCounter(VideoCommon::QueryType type) { 424void RasterizerOpenGL::ResetCounter(VideoCommon::QueryType type) {
404 if (type == VideoCommon::QueryType::ZPassPixelCount64) { 425 const auto query_cache_type = MaxwellToVideoCoreQuery(type);
405 query_cache.ResetCounter(VideoCore::QueryType::SamplesPassed); 426 if (!query_cache_type.has_value()) {
427 UNIMPLEMENTED_IF_MSG(type != VideoCommon::QueryType::Payload, "Reset query type: {}", type);
428 return;
406 } 429 }
430 query_cache.ResetCounter(*query_cache_type);
407} 431}
408 432
409void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, 433void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
410 VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) { 434 VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport) {
411 if (type == VideoCommon::QueryType::ZPassPixelCount64) { 435 const auto query_cache_type = MaxwellToVideoCoreQuery(type);
412 if (True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout)) { 436 if (!query_cache_type.has_value()) {
413 query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, {gpu.GetTicks()}); 437 return QueryFallback(gpu_addr, type, flags, payload, subreport);
414 } else {
415 query_cache.Query(gpu_addr, VideoCore::QueryType::SamplesPassed, std::nullopt);
416 }
417 return;
418 } 438 }
439 const bool has_timeout = True(flags & VideoCommon::QueryPropertiesFlags::HasTimeout);
440 const auto timestamp = has_timeout ? std::optional<u64>{gpu.GetTicks()} : std::nullopt;
441 query_cache.Query(gpu_addr, *query_cache_type, timestamp);
442}
443
444void RasterizerOpenGL::QueryFallback(GPUVAddr gpu_addr, VideoCommon::QueryType type,
445 VideoCommon::QueryPropertiesFlags flags, u32 payload,
446 u32 subreport) {
419 if (type != VideoCommon::QueryType::Payload) { 447 if (type != VideoCommon::QueryType::Payload) {
420 payload = 1u; 448 payload = 1u;
421 } 449 }
@@ -1350,6 +1378,10 @@ void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
1350 query_cache.EraseChannel(channel_id); 1378 query_cache.EraseChannel(channel_id);
1351} 1379}
1352 1380
1381void RasterizerOpenGL::RegisterTransformFeedback(GPUVAddr tfb_object_addr) {
1382 buffer_cache_runtime.BindTransformFeedbackObject(tfb_object_addr);
1383}
1384
1353AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_) 1385AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_, TextureCache& texture_cache_)
1354 : buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {} 1386 : buffer_cache{buffer_cache_}, texture_cache{texture_cache_} {}
1355 1387
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ceffe1f1e..ce3460938 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -139,6 +139,12 @@ public:
139 139
140 void ReleaseChannel(s32 channel_id) override; 140 void ReleaseChannel(s32 channel_id) override;
141 141
142 void RegisterTransformFeedback(GPUVAddr tfb_object_addr) override;
143
144 bool HasDrawTransformFeedback() override {
145 return true;
146 }
147
142private: 148private:
143 static constexpr size_t MAX_TEXTURES = 192; 149 static constexpr size_t MAX_TEXTURES = 192;
144 static constexpr size_t MAX_IMAGES = 48; 150 static constexpr size_t MAX_IMAGES = 48;
@@ -225,6 +231,9 @@ private:
225 /// End a transform feedback 231 /// End a transform feedback
226 void EndTransformFeedback(); 232 void EndTransformFeedback();
227 233
234 void QueryFallback(GPUVAddr gpu_addr, VideoCommon::QueryType type,
235 VideoCommon::QueryPropertiesFlags flags, u32 payload, u32 subreport);
236
228 Tegra::GPU& gpu; 237 Tegra::GPU& gpu;
229 238
230 const Device& device; 239 const Device& device;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index eae8fd110..1d2c9b70a 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -207,4 +207,21 @@ void OGLQuery::Release() {
207 handle = 0; 207 handle = 0;
208} 208}
209 209
210void OGLTransformFeedback::Create() {
211 if (handle != 0)
212 return;
213
214 MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
215 glCreateTransformFeedbacks(1, &handle);
216}
217
218void OGLTransformFeedback::Release() {
219 if (handle == 0)
220 return;
221
222 MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
223 glDeleteTransformFeedbacks(1, &handle);
224 handle = 0;
225}
226
210} // namespace OpenGL 227} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 77362acd2..6ca8227bd 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -323,4 +323,31 @@ public:
323 GLuint handle = 0; 323 GLuint handle = 0;
324}; 324};
325 325
326class OGLTransformFeedback final {
327public:
328 YUZU_NON_COPYABLE(OGLTransformFeedback);
329
330 OGLTransformFeedback() = default;
331
332 OGLTransformFeedback(OGLTransformFeedback&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
333
334 ~OGLTransformFeedback() {
335 Release();
336 }
337
338 OGLTransformFeedback& operator=(OGLTransformFeedback&& o) noexcept {
339 Release();
340 handle = std::exchange(o.handle, 0);
341 return *this;
342 }
343
344 /// Creates a new internal OpenGL resource and stores the handle
345 void Create();
346
347 /// Deletes the internal OpenGL resource
348 void Release();
349
350 GLuint handle = 0;
351};
352
326} // namespace OpenGL 353} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 26f2d0ea7..30df41b7d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
51using VideoCommon::SerializePipeline; 51using VideoCommon::SerializePipeline;
52using Context = ShaderContext::Context; 52using Context = ShaderContext::Context;
53 53
54constexpr u32 CACHE_VERSION = 9; 54constexpr u32 CACHE_VERSION = 10;
55 55
56template <typename Container> 56template <typename Container>
57auto MakeSpan(Container& container) { 57auto MakeSpan(Container& container) {
@@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
233 .ignore_nan_fp_comparisons = true, 233 .ignore_nan_fp_comparisons = true,
234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), 234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
235 .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), 235 .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
236 .max_user_clip_distances = 8,
236 }, 237 },
237 host_info{ 238 host_info{
238 .support_float64 = true, 239 .support_float64 = true,
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5958f52f7..3c61799fa 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -78,8 +78,15 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
78} 78}
79} // Anonymous namespace 79} // Anonymous namespace
80 80
81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) 81Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {} 82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {
83 if (runtime.device.HasNullDescriptor()) {
84 return;
85 }
86 device = &runtime.device;
87 buffer = runtime.CreateNullBuffer();
88 is_null = true;
89}
83 90
84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, 91Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
85 VAddr cpu_addr_, u64 size_bytes_) 92 VAddr cpu_addr_, u64 size_bytes_)
@@ -93,8 +100,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
93 100
94VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) { 101VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
95 if (!device) { 102 if (!device) {
96 // Null buffer, return a null descriptor 103 // Null buffer supported, return a null descriptor
97 return VK_NULL_HANDLE; 104 return VK_NULL_HANDLE;
105 } else if (is_null) {
106 // Null buffer not supported, adjust offset and size
107 offset = 0;
108 size = 0;
98 } 109 }
99 const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) { 110 const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
100 return offset == view.offset && size == view.size && format == view.format; 111 return offset == view.offset && size == view.size && format == view.format;
@@ -563,22 +574,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
563 } 574 }
564 buffer_handles.push_back(handle); 575 buffer_handles.push_back(handle);
565 } 576 }
577 const u32 device_max = device.GetMaxVertexInputBindings();
578 const u32 min_binding = std::min(bindings.min_index, device_max);
579 const u32 max_binding = std::min(bindings.max_index, device_max);
580 const u32 binding_count = max_binding - min_binding;
581 if (binding_count == 0) {
582 return;
583 }
566 if (device.IsExtExtendedDynamicStateSupported()) { 584 if (device.IsExtExtendedDynamicStateSupported()) {
567 scheduler.Record([this, bindings_ = std::move(bindings), 585 scheduler.Record([bindings_ = std::move(bindings),
568 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 586 buffer_handles_ = std::move(buffer_handles),
569 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, 587 binding_count](vk::CommandBuffer cmdbuf) {
570 std::min(bindings_.max_index - bindings_.min_index, 588 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
571 device.GetMaxVertexInputBindings()), 589 bindings_.offsets.data(), bindings_.sizes.data(),
572 buffer_handles_.data(), bindings_.offsets.data(), 590 bindings_.strides.data());
573 bindings_.sizes.data(), bindings_.strides.data());
574 }); 591 });
575 } else { 592 } else {
576 scheduler.Record([this, bindings_ = std::move(bindings), 593 scheduler.Record([bindings_ = std::move(bindings),
577 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 594 buffer_handles_ = std::move(buffer_handles),
578 cmdbuf.BindVertexBuffers(bindings_.min_index, 595 binding_count](vk::CommandBuffer cmdbuf) {
579 std::min(bindings_.max_index - bindings_.min_index, 596 cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
580 device.GetMaxVertexInputBindings()), 597 bindings_.offsets.data());
581 buffer_handles_.data(), bindings_.offsets.data());
582 }); 598 });
583 } 599 }
584} 600}
@@ -622,9 +638,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
622} 638}
623 639
624void BufferCacheRuntime::ReserveNullBuffer() { 640void BufferCacheRuntime::ReserveNullBuffer() {
625 if (null_buffer) { 641 if (!null_buffer) {
626 return; 642 null_buffer = CreateNullBuffer();
627 } 643 }
644}
645
646vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
628 VkBufferCreateInfo create_info{ 647 VkBufferCreateInfo create_info{
629 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 648 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
630 .pNext = nullptr, 649 .pNext = nullptr,
@@ -639,15 +658,17 @@ void BufferCacheRuntime::ReserveNullBuffer() {
639 if (device.IsExtTransformFeedbackSupported()) { 658 if (device.IsExtTransformFeedbackSupported()) {
640 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 659 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
641 } 660 }
642 null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal); 661 vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
643 if (device.HasDebuggingToolAttached()) { 662 if (device.HasDebuggingToolAttached()) {
644 null_buffer.SetObjectNameEXT("Null buffer"); 663 ret.SetObjectNameEXT("Null buffer");
645 } 664 }
646 665
647 scheduler.RequestOutsideRenderPassOperationContext(); 666 scheduler.RequestOutsideRenderPassOperationContext();
648 scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) { 667 scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) {
649 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); 668 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
650 }); 669 });
670
671 return ret;
651} 672}
652 673
653} // namespace Vulkan 674} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 0b3fbd6d0..dc300d7cb 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -63,6 +63,7 @@ private:
63 vk::Buffer buffer; 63 vk::Buffer buffer;
64 std::vector<BufferView> views; 64 std::vector<BufferView> views;
65 VideoCommon::UsageTracker tracker; 65 VideoCommon::UsageTracker tracker;
66 bool is_null{};
66}; 67};
67 68
68class QuadArrayIndexBuffer; 69class QuadArrayIndexBuffer;
@@ -151,6 +152,7 @@ private:
151 } 152 }
152 153
153 void ReserveNullBuffer(); 154 void ReserveNullBuffer();
155 vk::Buffer CreateNullBuffer();
154 156
155 const Device& device; 157 const Device& device;
156 MemoryAllocator& memory_allocator; 158 MemoryAllocator& memory_allocator;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 2a13b2a72..d1841198d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -54,7 +54,7 @@ using VideoCommon::FileEnvironment;
54using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
55using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
56 56
57constexpr u32 CACHE_VERSION = 10; 57constexpr u32 CACHE_VERSION = 11;
58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; 58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
59 59
60template <typename Container> 60template <typename Container>
@@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
374 .has_broken_robust = 374 .has_broken_robust =
375 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, 375 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
376 .min_ssbo_alignment = device.GetStorageBufferAlignment(), 376 .min_ssbo_alignment = device.GetStorageBufferAlignment(),
377 .max_user_clip_distances = device.GetMaxUserClipDistances(),
377 }; 378 };
378 379
379 host_info = Shader::HostTranslateInfo{ 380 host_info = Shader::HostTranslateInfo{
diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp
index 5e7518d96..792ed9615 100644
--- a/src/video_core/renderer_vulkan/vk_present_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp
@@ -329,7 +329,7 @@ void PresentManager::CopyToSwapchainImpl(Frame* frame) {
329 // to account for that. 329 // to account for that.
330 const bool is_suboptimal = swapchain.NeedsRecreation(); 330 const bool is_suboptimal = swapchain.NeedsRecreation();
331 const bool size_changed = 331 const bool size_changed =
332 swapchain.GetWidth() != frame->width || swapchain.GetHeight() != frame->height; 332 swapchain.GetWidth() < frame->width || swapchain.GetHeight() < frame->height;
333 if (is_suboptimal || size_changed) { 333 if (is_suboptimal || size_changed) {
334 RecreateSwapchain(frame); 334 RecreateSwapchain(frame);
335 } 335 }
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 078777cdd..95954ade7 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -289,12 +289,15 @@ public:
289 } 289 }
290 290
291 if (has_multi_queries) { 291 if (has_multi_queries) {
292 size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used); 292 const size_t min_accumulation_limit =
293 std::min(first_accumulation_checkpoint, num_slots_used);
294 const size_t max_accumulation_limit =
295 std::max(last_accumulation_checkpoint, num_slots_used);
296 const size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
293 resolve_buffers.push_back(intermediary_buffer_index); 297 resolve_buffers.push_back(intermediary_buffer_index);
294 queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index], 298 queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
295 *buffers[resolve_buffer_index], num_slots_used, 299 *buffers[resolve_buffer_index], num_slots_used,
296 std::min(first_accumulation_checkpoint, num_slots_used), 300 min_accumulation_limit, max_accumulation_limit);
297 last_accumulation_checkpoint);
298 301
299 } else { 302 } else {
300 scheduler.RequestOutsideRenderPassOperationContext(); 303 scheduler.RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 59829c88b..241fc34be 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -485,6 +485,10 @@ void RasterizerVulkan::DispatchCompute() {
485} 485}
486 486
487void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) { 487void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) {
488 if (type != VideoCommon::QueryType::ZPassPixelCount64) {
489 LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type);
490 return;
491 }
488 query_cache.CounterReset(type); 492 query_cache.CounterReset(type);
489} 493}
490 494
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 4edbe5700..492440ac4 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -62,23 +62,9 @@ static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& en
62} 62}
63 63
64static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) { 64static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
65 switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type, 65 return static_cast<Shader::TexturePixelFormat>(
66 entry.a_type, entry.srgb_conversion)) { 66 PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
67 case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM: 67 entry.a_type, entry.srgb_conversion));
68 return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
69 case VideoCore::Surface::PixelFormat::R8_SNORM:
70 return Shader::TexturePixelFormat::R8_SNORM;
71 case VideoCore::Surface::PixelFormat::R8G8_SNORM:
72 return Shader::TexturePixelFormat::R8G8_SNORM;
73 case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
74 return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
75 case VideoCore::Surface::PixelFormat::R16G16_SNORM:
76 return Shader::TexturePixelFormat::R16G16_SNORM;
77 case VideoCore::Surface::PixelFormat::R16_SNORM:
78 return Shader::TexturePixelFormat::R16_SNORM;
79 default:
80 return Shader::TexturePixelFormat::OTHER;
81 }
82} 68}
83 69
84static std::string_view StageToPrefix(Shader::Stage stage) { 70static std::string_view StageToPrefix(Shader::Stage stage) {
@@ -398,6 +384,11 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
398 return result; 384 return result;
399} 385}
400 386
387bool GraphicsEnvironment::IsTexturePixelFormatInteger(u32 handle) {
388 return VideoCore::Surface::IsPixelFormatInteger(
389 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
390}
391
401u32 GraphicsEnvironment::ReadViewportTransformState() { 392u32 GraphicsEnvironment::ReadViewportTransformState() {
402 const auto& regs{maxwell3d->regs}; 393 const auto& regs{maxwell3d->regs};
403 viewport_transform_state = regs.viewport_scale_offset_enabled; 394 viewport_transform_state = regs.viewport_scale_offset_enabled;
@@ -448,6 +439,11 @@ Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle
448 return result; 439 return result;
449} 440}
450 441
442bool ComputeEnvironment::IsTexturePixelFormatInteger(u32 handle) {
443 return VideoCore::Surface::IsPixelFormatInteger(
444 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
445}
446
451u32 ComputeEnvironment::ReadViewportTransformState() { 447u32 ComputeEnvironment::ReadViewportTransformState() {
452 return viewport_transform_state; 448 return viewport_transform_state;
453} 449}
@@ -551,6 +547,11 @@ Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
551 return it->second; 547 return it->second;
552} 548}
553 549
550bool FileEnvironment::IsTexturePixelFormatInteger(u32 handle) {
551 return VideoCore::Surface::IsPixelFormatInteger(
552 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
553}
554
554u32 FileEnvironment::ReadViewportTransformState() { 555u32 FileEnvironment::ReadViewportTransformState() {
555 return viewport_transform_state; 556 return viewport_transform_state;
556} 557}
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index b90f3d44e..6b372e336 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -115,6 +115,8 @@ public:
115 115
116 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 116 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
117 117
118 bool IsTexturePixelFormatInteger(u32 handle) override;
119
118 u32 ReadViewportTransformState() override; 120 u32 ReadViewportTransformState() override;
119 121
120 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override; 122 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
@@ -139,6 +141,8 @@ public:
139 141
140 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 142 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
141 143
144 bool IsTexturePixelFormatInteger(u32 handle) override;
145
142 u32 ReadViewportTransformState() override; 146 u32 ReadViewportTransformState() override;
143 147
144 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer( 148 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
@@ -171,6 +175,8 @@ public:
171 175
172 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 176 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
173 177
178 [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
179
174 [[nodiscard]] u32 ReadViewportTransformState() override; 180 [[nodiscard]] u32 ReadViewportTransformState() override;
175 181
176 [[nodiscard]] u32 LocalMemorySize() const override; 182 [[nodiscard]] u32 LocalMemorySize() const override;
diff --git a/src/video_core/texture_cache/decode_bc.cpp b/src/video_core/texture_cache/decode_bc.cpp
index 3e26474a3..a018c6df4 100644
--- a/src/video_core/texture_cache/decode_bc.cpp
+++ b/src/video_core/texture_cache/decode_bc.cpp
@@ -60,66 +60,72 @@ u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
60} 60}
61 61
62template <auto decompress, PixelFormat pixel_format> 62template <auto decompress, PixelFormat pixel_format>
63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, Extent3D extent, 63void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
64 bool is_signed = false) { 64 bool is_signed = false) {
65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format); 65 const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
66 const u32 block_width = std::min(extent.width, BLOCK_SIZE); 66 const u32 block_size = BlockSize(pixel_format);
67 const u32 block_height = std::min(extent.height, BLOCK_SIZE); 67 const u32 width = copy.image_extent.width;
68 const u32 pitch = extent.width * out_bpp; 68 const u32 height = copy.image_extent.height * copy.image_subresource.num_layers;
69 const u32 depth = copy.image_extent.depth;
70 const u32 block_width = std::min(width, BLOCK_SIZE);
71 const u32 block_height = std::min(height, BLOCK_SIZE);
72 const u32 pitch = width * out_bpp;
69 size_t input_offset = 0; 73 size_t input_offset = 0;
70 size_t output_offset = 0; 74 size_t output_offset = 0;
71 for (u32 slice = 0; slice < extent.depth; ++slice) { 75 for (u32 slice = 0; slice < depth; ++slice) {
72 for (u32 y = 0; y < extent.height; y += block_height) { 76 for (u32 y = 0; y < height; y += block_height) {
73 size_t row_offset = 0; 77 size_t src_offset = input_offset;
74 for (u32 x = 0; x < extent.width; 78 size_t dst_offset = output_offset;
75 x += block_width, row_offset += block_width * out_bpp) { 79 for (u32 x = 0; x < width; x += block_width) {
76 const u8* src = input.data() + input_offset; 80 const u8* src = input.data() + src_offset;
77 u8* const dst = output.data() + output_offset + row_offset; 81 u8* const dst = output.data() + dst_offset;
78 if constexpr (IsSigned(pixel_format)) { 82 if constexpr (IsSigned(pixel_format)) {
79 decompress(src, dst, x, y, extent.width, extent.height, is_signed); 83 decompress(src, dst, x, y, width, height, is_signed);
80 } else { 84 } else {
81 decompress(src, dst, x, y, extent.width, extent.height); 85 decompress(src, dst, x, y, width, height);
82 } 86 }
83 input_offset += BlockSize(pixel_format); 87 src_offset += block_size;
88 dst_offset += block_width * out_bpp;
84 } 89 }
90 input_offset += copy.buffer_row_length * block_size / block_width;
85 output_offset += block_height * pitch; 91 output_offset += block_height * pitch;
86 } 92 }
87 } 93 }
88} 94}
89 95
90void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, 96void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
91 VideoCore::Surface::PixelFormat pixel_format) { 97 VideoCore::Surface::PixelFormat pixel_format) {
92 switch (pixel_format) { 98 switch (pixel_format) {
93 case PixelFormat::BC1_RGBA_UNORM: 99 case PixelFormat::BC1_RGBA_UNORM:
94 case PixelFormat::BC1_RGBA_SRGB: 100 case PixelFormat::BC1_RGBA_SRGB:
95 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, extent); 101 DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
96 break; 102 break;
97 case PixelFormat::BC2_UNORM: 103 case PixelFormat::BC2_UNORM:
98 case PixelFormat::BC2_SRGB: 104 case PixelFormat::BC2_SRGB:
99 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, extent); 105 DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
100 break; 106 break;
101 case PixelFormat::BC3_UNORM: 107 case PixelFormat::BC3_UNORM:
102 case PixelFormat::BC3_SRGB: 108 case PixelFormat::BC3_SRGB:
103 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, extent); 109 DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
104 break; 110 break;
105 case PixelFormat::BC4_SNORM: 111 case PixelFormat::BC4_SNORM:
106 case PixelFormat::BC4_UNORM: 112 case PixelFormat::BC4_UNORM:
107 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>( 113 DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
108 input, output, extent, pixel_format == PixelFormat::BC4_SNORM); 114 input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
109 break; 115 break;
110 case PixelFormat::BC5_SNORM: 116 case PixelFormat::BC5_SNORM:
111 case PixelFormat::BC5_UNORM: 117 case PixelFormat::BC5_UNORM:
112 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>( 118 DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
113 input, output, extent, pixel_format == PixelFormat::BC5_SNORM); 119 input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
114 break; 120 break;
115 case PixelFormat::BC6H_SFLOAT: 121 case PixelFormat::BC6H_SFLOAT:
116 case PixelFormat::BC6H_UFLOAT: 122 case PixelFormat::BC6H_UFLOAT:
117 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>( 123 DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
118 input, output, extent, pixel_format == PixelFormat::BC6H_SFLOAT); 124 input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
119 break; 125 break;
120 case PixelFormat::BC7_SRGB: 126 case PixelFormat::BC7_SRGB:
121 case PixelFormat::BC7_UNORM: 127 case PixelFormat::BC7_UNORM:
122 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, extent); 128 DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
123 break; 129 break;
124 default: 130 default:
125 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format); 131 LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
diff --git a/src/video_core/texture_cache/decode_bc.h b/src/video_core/texture_cache/decode_bc.h
index 41d1ec0a3..4e3b9b8ac 100644
--- a/src/video_core/texture_cache/decode_bc.h
+++ b/src/video_core/texture_cache/decode_bc.h
@@ -13,7 +13,7 @@ namespace VideoCommon {
13 13
14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format); 14[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);
15 15
16void DecompressBCn(std::span<const u8> input, std::span<u8> output, Extent3D extent, 16void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
17 VideoCore::Surface::PixelFormat pixel_format); 17 VideoCore::Surface::PixelFormat pixel_format);
18 18
19} // namespace VideoCommon 19} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index 15596c925..fcf70068e 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -837,6 +837,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
837 std::span<u8> output) { 837 std::span<u8> output) {
838 const size_t guest_size_bytes = input.size_bytes(); 838 const size_t guest_size_bytes = input.size_bytes();
839 const u32 bpp_log2 = BytesPerBlockLog2(info.format); 839 const u32 bpp_log2 = BytesPerBlockLog2(info.format);
840 const Extent2D tile_size = DefaultBlockSize(info.format);
840 const Extent3D size = info.size; 841 const Extent3D size = info.size;
841 842
842 if (info.type == ImageType::Linear) { 843 if (info.type == ImageType::Linear) {
@@ -847,7 +848,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
847 return {{ 848 return {{
848 .buffer_offset = 0, 849 .buffer_offset = 0,
849 .buffer_size = guest_size_bytes, 850 .buffer_size = guest_size_bytes,
850 .buffer_row_length = info.pitch >> bpp_log2, 851 .buffer_row_length = info.pitch * tile_size.width >> bpp_log2,
851 .buffer_image_height = size.height, 852 .buffer_image_height = size.height,
852 .image_subresource = 853 .image_subresource =
853 { 854 {
@@ -862,7 +863,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
862 const LevelInfo level_info = MakeLevelInfo(info); 863 const LevelInfo level_info = MakeLevelInfo(info);
863 const s32 num_layers = info.resources.layers; 864 const s32 num_layers = info.resources.layers;
864 const s32 num_levels = info.resources.levels; 865 const s32 num_levels = info.resources.levels;
865 const Extent2D tile_size = DefaultBlockSize(info.format);
866 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); 866 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
867 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); 867 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
868 const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels); 868 const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
@@ -926,8 +926,6 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
926 926
927 const auto input_offset = input.subspan(copy.buffer_offset); 927 const auto input_offset = input.subspan(copy.buffer_offset);
928 copy.buffer_offset = output_offset; 928 copy.buffer_offset = output_offset;
929 copy.buffer_row_length = mip_size.width;
930 copy.buffer_image_height = mip_size.height;
931 929
932 const auto recompression_setting = Settings::values.astc_recompression.GetValue(); 930 const auto recompression_setting = Settings::values.astc_recompression.GetValue();
933 const bool astc = IsPixelFormatASTC(info.format); 931 const bool astc = IsPixelFormatASTC(info.format);
@@ -972,16 +970,14 @@ void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8
972 bpp_div; 970 bpp_div;
973 output_offset += static_cast<u32>(copy.buffer_size); 971 output_offset += static_cast<u32>(copy.buffer_size);
974 } else { 972 } else {
975 const Extent3D image_extent{ 973 DecompressBCn(input_offset, output.subspan(output_offset), copy, info.format);
976 .width = copy.image_extent.width,
977 .height = copy.image_extent.height * copy.image_subresource.num_layers,
978 .depth = copy.image_extent.depth,
979 };
980 DecompressBCn(input_offset, output.subspan(output_offset), image_extent, info.format);
981 output_offset += copy.image_extent.width * copy.image_extent.height * 974 output_offset += copy.image_extent.width * copy.image_extent.height *
982 copy.image_subresource.num_layers * 975 copy.image_subresource.num_layers *
983 ConvertedBytesPerBlock(info.format); 976 ConvertedBytesPerBlock(info.format);
984 } 977 }
978
979 copy.buffer_row_length = mip_size.width;
980 copy.buffer_image_height = mip_size.height;
985 } 981 }
986} 982}
987 983
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 1fda0042d..727bbd98d 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -695,6 +695,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
695 std::min(properties.properties.limits.maxVertexInputBindings, 16U); 695 std::min(properties.properties.limits.maxVertexInputBindings, 16U);
696 } 696 }
697 697
698 if (is_turnip) {
699 LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
700 properties.properties.limits.maxVertexInputBindings = 32;
701 }
702
698 if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) { 703 if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
699 LOG_INFO(Render_Vulkan, 704 LOG_INFO(Render_Vulkan,
700 "Removing extendedDynamicState2 due to missing extendedDynamicState"); 705 "Removing extendedDynamicState2 due to missing extendedDynamicState");
@@ -750,10 +755,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
750 // The wanted format is not supported by hardware, search for alternatives 755 // The wanted format is not supported by hardware, search for alternatives
751 const VkFormat* alternatives = GetFormatAlternatives(wanted_format); 756 const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
752 if (alternatives == nullptr) { 757 if (alternatives == nullptr) {
753 ASSERT_MSG(false, 758 LOG_ERROR(Render_Vulkan,
754 "Format={} with usage={} and type={} has no defined alternatives and host " 759 "Format={} with usage={} and type={} has no defined alternatives and host "
755 "hardware does not support it", 760 "hardware does not support it",
756 wanted_format, wanted_usage, format_type); 761 wanted_format, wanted_usage, format_type);
757 return wanted_format; 762 return wanted_format;
758 } 763 }
759 764
@@ -769,10 +774,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
769 } 774 }
770 775
771 // No alternatives found, panic 776 // No alternatives found, panic
772 ASSERT_MSG(false, 777 LOG_ERROR(Render_Vulkan,
773 "Format={} with usage={} and type={} is not supported by the host hardware and " 778 "Format={} with usage={} and type={} is not supported by the host hardware and "
774 "doesn't support any of the alternatives", 779 "doesn't support any of the alternatives",
775 wanted_format, wanted_usage, format_type); 780 wanted_format, wanted_usage, format_type);
776 return wanted_format; 781 return wanted_format;
777} 782}
778 783
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 4f3846345..701817086 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -665,6 +665,10 @@ public:
665 return properties.properties.limits.maxViewports; 665 return properties.properties.limits.maxViewports;
666 } 666 }
667 667
668 u32 GetMaxUserClipDistances() const {
669 return properties.properties.limits.maxClipDistances;
670 }
671
668 bool SupportsConditionalBarriers() const { 672 bool SupportsConditionalBarriers() const {
669 return supports_conditional_barriers; 673 return supports_conditional_barriers;
670 } 674 }
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 70cf14afa..074aed964 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -246,7 +246,9 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec
246 .objectHandle = reinterpret_cast<u64>(handle), 246 .objectHandle = reinterpret_cast<u64>(handle),
247 .pObjectName = name, 247 .pObjectName = name,
248 }; 248 };
249 Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info)); 249 if (dld->vkSetDebugUtilsObjectNameEXT) {
250 Check(dld->vkSetDebugUtilsObjectNameEXT(device, &name_info));
251 }
250} 252}
251 253
252} // Anonymous namespace 254} // Anonymous namespace
@@ -377,6 +379,8 @@ const char* ToString(VkResult result) noexcept {
377 return "VK_OPERATION_DEFERRED_KHR"; 379 return "VK_OPERATION_DEFERRED_KHR";
378 case VkResult::VK_OPERATION_NOT_DEFERRED_KHR: 380 case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
379 return "VK_OPERATION_NOT_DEFERRED_KHR"; 381 return "VK_OPERATION_NOT_DEFERRED_KHR";
382 case VkResult::VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR:
383 return "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
380 case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT: 384 case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
381 return "VK_PIPELINE_COMPILE_REQUIRED_EXT"; 385 return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
382 case VkResult::VK_RESULT_MAX_ENUM: 386 case VkResult::VK_RESULT_MAX_ENUM:
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index fd6bebf0f..0836bcb7e 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -106,32 +106,30 @@ ConfigureGraphics::ConfigureGraphics(
106 Settings::values.bg_green.GetValue(), 106 Settings::values.bg_green.GetValue(),
107 Settings::values.bg_blue.GetValue())); 107 Settings::values.bg_blue.GetValue()));
108 UpdateAPILayout(); 108 UpdateAPILayout();
109 PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout 109 PopulateVSyncModeSelection(false); //< must happen after UpdateAPILayout
110 110
111 // VSync setting needs to be determined after populating the VSync combobox 111 // VSync setting needs to be determined after populating the VSync combobox
112 if (Settings::IsConfiguringGlobal()) { 112 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
113 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); 113 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
114 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); 114 int index{};
115 int index{}; 115 for (const auto mode : vsync_mode_combobox_enum_map) {
116 for (const auto mode : vsync_mode_combobox_enum_map) { 116 if (mode == vsync_mode) {
117 if (mode == vsync_mode) { 117 break;
118 break;
119 }
120 index++;
121 }
122 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
123 vsync_mode_combobox->setCurrentIndex(index);
124 } 118 }
119 index++;
120 }
121 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
122 vsync_mode_combobox->setCurrentIndex(index);
125 } 123 }
126 124
127 connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] { 125 connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
128 UpdateAPILayout(); 126 UpdateAPILayout();
129 PopulateVSyncModeSelection(); 127 PopulateVSyncModeSelection(false);
130 }); 128 });
131 connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this, 129 connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
132 [this](int device) { 130 [this](int device) {
133 UpdateDeviceSelection(device); 131 UpdateDeviceSelection(device);
134 PopulateVSyncModeSelection(); 132 PopulateVSyncModeSelection(false);
135 }); 133 });
136 connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this, 134 connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
137 [this](int backend) { UpdateShaderBackendSelection(backend); }); 135 [this](int backend) { UpdateShaderBackendSelection(backend); });
@@ -147,8 +145,9 @@ ConfigureGraphics::ConfigureGraphics(
147 const auto& update_screenshot_info = [this, &builder]() { 145 const auto& update_screenshot_info = [this, &builder]() {
148 const auto& combobox_enumerations = builder.ComboboxTranslations().at( 146 const auto& combobox_enumerations = builder.ComboboxTranslations().at(
149 Settings::EnumMetadata<Settings::AspectRatio>::Index()); 147 Settings::EnumMetadata<Settings::AspectRatio>::Index());
150 const auto index = aspect_ratio_combobox->currentIndex(); 148 const auto ratio_index = aspect_ratio_combobox->currentIndex();
151 const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first); 149 const auto ratio =
150 static_cast<Settings::AspectRatio>(combobox_enumerations[ratio_index].first);
152 151
153 const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at( 152 const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
154 Settings::EnumMetadata<Settings::ResolutionSetup>::Index()); 153 Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
@@ -174,11 +173,7 @@ ConfigureGraphics::ConfigureGraphics(
174 } 173 }
175} 174}
176 175
177void ConfigureGraphics::PopulateVSyncModeSelection() { 176void ConfigureGraphics::PopulateVSyncModeSelection(bool use_setting) {
178 if (!Settings::IsConfiguringGlobal()) {
179 return;
180 }
181
182 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; 177 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
183 if (backend == Settings::RendererBackend::Null) { 178 if (backend == Settings::RendererBackend::Null) {
184 vsync_mode_combobox->setEnabled(false); 179 vsync_mode_combobox->setEnabled(false);
@@ -189,8 +184,9 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
189 const int current_index = //< current selected vsync mode from combobox 184 const int current_index = //< current selected vsync mode from combobox
190 vsync_mode_combobox->currentIndex(); 185 vsync_mode_combobox->currentIndex();
191 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR 186 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
192 current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) 187 current_index == -1 || use_setting
193 : vsync_mode_combobox_enum_map[current_index]; 188 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
189 : vsync_mode_combobox_enum_map[current_index];
194 int index{}; 190 int index{};
195 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device 191 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
196 192
@@ -214,6 +210,23 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
214 } 210 }
215 index++; 211 index++;
216 } 212 }
213
214 if (!Settings::IsConfiguringGlobal()) {
215 vsync_restore_global_button->setVisible(!Settings::values.vsync_mode.UsingGlobal());
216
217 const Settings::VSyncMode global_vsync_mode = Settings::values.vsync_mode.GetValue(true);
218 vsync_restore_global_button->setEnabled(
219 (backend == Settings::RendererBackend::OpenGL &&
220 (global_vsync_mode == Settings::VSyncMode::Immediate ||
221 global_vsync_mode == Settings::VSyncMode::Fifo)) ||
222 backend == Settings::RendererBackend::Vulkan);
223 }
224}
225
226void ConfigureGraphics::UpdateVsyncSetting() const {
227 const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
228 const auto vsync_mode = PresentModeToSetting(mode);
229 Settings::values.vsync_mode.SetValue(vsync_mode);
217} 230}
218 231
219void ConfigureGraphics::UpdateDeviceSelection(int device) { 232void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -299,6 +312,33 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
299 } else if (setting->Id() == Settings::values.vsync_mode.Id()) { 312 } else if (setting->Id() == Settings::values.vsync_mode.Id()) {
300 // Keep track of vsync_mode's combobox so we can populate it 313 // Keep track of vsync_mode's combobox so we can populate it
301 vsync_mode_combobox = widget->combobox; 314 vsync_mode_combobox = widget->combobox;
315
316 // Since vsync is populated at runtime, we have to manually set up the button for
317 // restoring the global setting.
318 if (!Settings::IsConfiguringGlobal()) {
319 QPushButton* restore_button =
320 ConfigurationShared::Widget::CreateRestoreGlobalButton(
321 Settings::values.vsync_mode.UsingGlobal(), widget);
322 restore_button->setEnabled(true);
323 widget->layout()->addWidget(restore_button);
324
325 QObject::connect(restore_button, &QAbstractButton::clicked,
326 [restore_button, this](bool) {
327 Settings::values.vsync_mode.SetGlobal(true);
328 PopulateVSyncModeSelection(true);
329
330 restore_button->setVisible(false);
331 });
332
333 std::function<void()> set_non_global = [restore_button, this]() {
334 Settings::values.vsync_mode.SetGlobal(false);
335 UpdateVsyncSetting();
336 restore_button->setVisible(true);
337 };
338 QObject::connect(widget->combobox, QOverload<int>::of(&QComboBox::activated),
339 [set_non_global]() { set_non_global(); });
340 vsync_restore_global_button = restore_button;
341 }
302 hold_graphics.emplace(setting->Id(), widget); 342 hold_graphics.emplace(setting->Id(), widget);
303 } else if (setting->Id() == Settings::values.aspect_ratio.Id()) { 343 } else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
304 // Keep track of the aspect ratio combobox to update other UI tabs that need it 344 // Keep track of the aspect ratio combobox to update other UI tabs that need it
@@ -400,11 +440,7 @@ void ConfigureGraphics::ApplyConfiguration() {
400 func(powered_on); 440 func(powered_on);
401 } 441 }
402 442
403 if (Settings::IsConfiguringGlobal()) { 443 UpdateVsyncSetting();
404 const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
405 const auto vsync_mode = PresentModeToSetting(mode);
406 Settings::values.vsync_mode.SetValue(vsync_mode);
407 }
408 444
409 Settings::values.vulkan_device.SetGlobal(true); 445 Settings::values.vulkan_device.SetGlobal(true);
410 Settings::values.shader_backend.SetGlobal(true); 446 Settings::values.shader_backend.SetGlobal(true);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 9c24a56db..5c8286836 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -62,7 +62,8 @@ private:
62 62
63 void Setup(const ConfigurationShared::Builder& builder); 63 void Setup(const ConfigurationShared::Builder& builder);
64 64
65 void PopulateVSyncModeSelection(); 65 void PopulateVSyncModeSelection(bool use_setting);
66 void UpdateVsyncSetting() const;
66 void UpdateBackgroundColorButton(QColor color); 67 void UpdateBackgroundColorButton(QColor color);
67 void UpdateAPILayout(); 68 void UpdateAPILayout();
68 void UpdateDeviceSelection(int device); 69 void UpdateDeviceSelection(int device);
@@ -104,6 +105,7 @@ private:
104 QComboBox* api_combobox; 105 QComboBox* api_combobox;
105 QComboBox* shader_backend_combobox; 106 QComboBox* shader_backend_combobox;
106 QComboBox* vsync_mode_combobox; 107 QComboBox* vsync_mode_combobox;
108 QPushButton* vsync_restore_global_button;
107 QWidget* vulkan_device_widget; 109 QWidget* vulkan_device_widget;
108 QWidget* api_widget; 110 QWidget* api_widget;
109 QWidget* shader_backend_widget; 111 QWidget* shader_backend_widget;
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index a71000b72..6aca71d7c 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -348,43 +348,45 @@ void QtConfig::SaveQtPlayerValues(const std::size_t player_index) {
348 348
349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 349 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 350 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
351 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), 351 WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
352 player.buttons[i], std::make_optional(default_param)); 352 player.buttons[i], std::make_optional(default_param));
353 } 353 }
354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 354 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 355 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 356 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
357 default_analogs[i][3], default_stick_mod[i], 0.5f); 357 default_analogs[i][3], default_stick_mod[i], 0.5f);
358 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), 358 WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
359 player.analogs[i], std::make_optional(default_param)); 359 player.analogs[i], std::make_optional(default_param));
360 } 360 }
361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 361 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 362 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
363 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), 363 WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
364 player.motions[i], std::make_optional(default_param)); 364 player.motions[i], std::make_optional(default_param));
365 } 365 }
366} 366}
367 367
368void QtConfig::SaveDebugControlValues() { 368void QtConfig::SaveDebugControlValues() {
369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 369 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 370 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
371 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), 371 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
372 Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); 372 Settings::values.debug_pad_buttons[i],
373 std::make_optional(default_param));
373 } 374 }
374 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 375 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
375 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 376 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
376 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 377 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
377 default_analogs[i][3], default_stick_mod[i], 0.5f); 378 default_analogs[i][3], default_stick_mod[i], 0.5f);
378 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), 379 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
379 Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); 380 Settings::values.debug_pad_analogs[i],
381 std::make_optional(default_param));
380 } 382 }
381} 383}
382 384
383void QtConfig::SaveHidbusValues() { 385void QtConfig::SaveHidbusValues() {
384 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 386 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
385 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 387 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
386 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, 388 WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
387 std::make_optional(default_param)); 389 std::make_optional(default_param));
388} 390}
389 391
390void QtConfig::SaveQtControlValues() { 392void QtConfig::SaveQtControlValues() {
@@ -409,19 +411,20 @@ void QtConfig::SavePathValues() {
409 411
410 WriteCategory(Settings::Category::Paths); 412 WriteCategory(Settings::Category::Paths);
411 413
412 WriteSetting(std::string("romsPath"), UISettings::values.roms_path); 414 WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path);
413 BeginArray(std::string("gamedirs")); 415 BeginArray(std::string("gamedirs"));
414 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { 416 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
415 SetArrayIndex(i); 417 SetArrayIndex(i);
416 const auto& game_dir = UISettings::values.game_dirs[i]; 418 const auto& game_dir = UISettings::values.game_dirs[i];
417 WriteSetting(std::string("path"), game_dir.path); 419 WriteStringSetting(std::string("path"), game_dir.path);
418 WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); 420 WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan,
419 WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); 421 std::make_optional(false));
422 WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true));
420 } 423 }
421 EndArray(); 424 EndArray();
422 425
423 WriteSetting(std::string("recentFiles"), 426 WriteStringSetting(std::string("recentFiles"),
424 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); 427 UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString());
425 428
426 EndGroup(); 429 EndGroup();
427} 430}
@@ -438,14 +441,14 @@ void QtConfig::SaveShortcutValues() {
438 BeginGroup(group); 441 BeginGroup(group);
439 BeginGroup(name); 442 BeginGroup(name);
440 443
441 WriteSetting(std::string("KeySeq"), shortcut.keyseq, 444 WriteStringSetting(std::string("KeySeq"), shortcut.keyseq,
442 std::make_optional(default_hotkey.keyseq)); 445 std::make_optional(default_hotkey.keyseq));
443 WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, 446 WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq,
444 std::make_optional(default_hotkey.controller_keyseq)); 447 std::make_optional(default_hotkey.controller_keyseq));
445 WriteSetting(std::string("Context"), shortcut.context, 448 WriteIntegerSetting(std::string("Context"), shortcut.context,
446 std::make_optional(default_hotkey.context)); 449 std::make_optional(default_hotkey.context));
447 WriteSetting(std::string("Repeat"), shortcut.repeat, 450 WriteBooleanSetting(std::string("Repeat"), shortcut.repeat,
448 std::make_optional(default_hotkey.repeat)); 451 std::make_optional(default_hotkey.repeat));
449 452
450 EndGroup(); // name 453 EndGroup(); // name
451 EndGroup(); // group 454 EndGroup(); // group
@@ -460,9 +463,10 @@ void QtConfig::SaveUIValues() {
460 WriteCategory(Settings::Category::Ui); 463 WriteCategory(Settings::Category::Ui);
461 WriteCategory(Settings::Category::UiGeneral); 464 WriteCategory(Settings::Category::UiGeneral);
462 465
463 WriteSetting(std::string("theme"), UISettings::values.theme, 466 WriteStringSetting(
464 std::make_optional(std::string( 467 std::string("theme"), UISettings::values.theme,
465 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second))); 468 std::make_optional(std::string(
469 UISettings::themes[static_cast<size_t>(UISettings::default_theme)].second)));
466 470
467 SaveUIGamelistValues(); 471 SaveUIGamelistValues();
468 SaveUILayoutValues(); 472 SaveUILayoutValues();
@@ -482,7 +486,7 @@ void QtConfig::SaveUIGamelistValues() {
482 BeginArray(std::string("favorites")); 486 BeginArray(std::string("favorites"));
483 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { 487 for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
484 SetArrayIndex(i); 488 SetArrayIndex(i);
485 WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); 489 WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
486 } 490 }
487 EndArray(); // favorites 491 EndArray(); // favorites
488 492
@@ -506,14 +510,15 @@ void QtConfig::SaveMultiplayerValues() {
506 BeginArray(std::string("username_ban_list")); 510 BeginArray(std::string("username_ban_list"));
507 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { 511 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) {
508 SetArrayIndex(static_cast<int>(i)); 512 SetArrayIndex(static_cast<int>(i));
509 WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); 513 WriteStringSetting(std::string("username"),
514 UISettings::values.multiplayer_ban_list.first[i]);
510 } 515 }
511 EndArray(); // username_ban_list 516 EndArray(); // username_ban_list
512 517
513 BeginArray(std::string("ip_ban_list")); 518 BeginArray(std::string("ip_ban_list"));
514 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { 519 for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) {
515 SetArrayIndex(static_cast<int>(i)); 520 SetArrayIndex(static_cast<int>(i));
516 WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); 521 WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]);
517 } 522 }
518 EndArray(); // ip_ban_list 523 EndArray(); // ip_ban_list
519 524
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 059fcf041..c789c1e59 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -5342,6 +5342,10 @@ int main(int argc, char* argv[]) {
5342 if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) { 5342 if (QString::fromLocal8Bit(qgetenv("DISPLAY")).isEmpty()) {
5343 qputenv("DISPLAY", ":0"); 5343 qputenv("DISPLAY", ":0");
5344 } 5344 }
5345
5346 // Fix the Wayland appId. This needs to match the name of the .desktop file without the .desktop
5347 // suffix.
5348 QGuiApplication::setDesktopFileName(QStringLiteral("org.yuzu_emu.yuzu"));
5345#endif 5349#endif
5346 5350
5347 SetHighDPIAttributes(); 5351 SetHighDPIAttributes();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 530e445f9..366e806d5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -168,14 +168,6 @@ class GMainWindow : public QMainWindow {
168 /// Max number of recently loaded items to keep track of 168 /// Max number of recently loaded items to keep track of
169 static const int max_recent_files_item = 10; 169 static const int max_recent_files_item = 10;
170 170
171 // TODO: Make use of this!
172 enum {
173 UI_IDLE,
174 UI_EMU_BOOTING,
175 UI_EMU_RUNNING,
176 UI_EMU_STOPPING,
177 };
178
179 enum { 171 enum {
180 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, 172 CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES,
181 CREATE_SHORTCUT_MSGBOX_SUCCESS, 173 CREATE_SHORTCUT_MSGBOX_SUCCESS,
diff --git a/src/yuzu/multiplayer/direct_connect.cpp b/src/yuzu/multiplayer/direct_connect.cpp
index a415a953f..74da97e21 100644
--- a/src/yuzu/multiplayer/direct_connect.cpp
+++ b/src/yuzu/multiplayer/direct_connect.cpp
@@ -94,7 +94,7 @@ void DirectConnectWindow::Connect() {
94 // Store settings 94 // Store settings
95 UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); 95 UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString();
96 UISettings::values.multiplayer_ip = ui->ip->text().toStdString(); 96 UISettings::values.multiplayer_ip = ui->ip->text().toStdString();
97 if (ui->port->isModified() && !ui->port->text().isEmpty()) { 97 if (!ui->port->text().isEmpty()) {
98 UISettings::values.multiplayer_port = ui->port->text().toInt(); 98 UISettings::values.multiplayer_port = ui->port->text().toInt();
99 } else { 99 } else {
100 UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault(); 100 UISettings::values.multiplayer_port = UISettings::values.multiplayer_port.GetDefault();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 5153cdb79..1a35d471c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -20,7 +20,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Co
20 : input_subsystem{input_subsystem_}, system{system_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 input_subsystem->Initialize(); 21 input_subsystem->Initialize();
22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) { 22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2: {}, Exiting...", SDL_GetError());
24 exit(1); 24 exit(1);
25 } 25 }
26 SDL_SetMainReady(); 26 SDL_SetMainReady();
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 9ed47d453..8b916f05c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -28,7 +28,8 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
28 SDL_SysWMinfo wm; 28 SDL_SysWMinfo wm;
29 SDL_VERSION(&wm.version); 29 SDL_VERSION(&wm.version);
30 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) { 30 if (SDL_GetWindowWMInfo(render_window, &wm) == SDL_FALSE) {
31 LOG_CRITICAL(Frontend, "Failed to get information from the window manager"); 31 LOG_CRITICAL(Frontend, "Failed to get information from the window manager: {}",
32 SDL_GetError());
32 std::exit(EXIT_FAILURE); 33 std::exit(EXIT_FAILURE);
33 } 34 }
34 35
diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp
index 39fd8050c..e81bf5d45 100644
--- a/src/yuzu_cmd/sdl_config.cpp
+++ b/src/yuzu_cmd/sdl_config.cpp
@@ -213,43 +213,45 @@ void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) {
213 213
214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 214 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 215 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
216 WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), 216 WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]),
217 player.buttons[i], std::make_optional(default_param)); 217 player.buttons[i], std::make_optional(default_param));
218 } 218 }
219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 219 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 220 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 221 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
222 default_analogs[i][3], default_stick_mod[i], 0.5f); 222 default_analogs[i][3], default_stick_mod[i], 0.5f);
223 WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), 223 WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]),
224 player.analogs[i], std::make_optional(default_param)); 224 player.analogs[i], std::make_optional(default_param));
225 } 225 }
226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { 226 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); 227 const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
228 WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), 228 WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]),
229 player.motions[i], std::make_optional(default_param)); 229 player.motions[i], std::make_optional(default_param));
230 } 230 }
231} 231}
232 232
233void SdlConfig::SaveDebugControlValues() { 233void SdlConfig::SaveDebugControlValues() {
234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { 234 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); 235 const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
236 WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), 236 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]),
237 Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); 237 Settings::values.debug_pad_buttons[i],
238 std::make_optional(default_param));
238 } 239 }
239 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 240 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
240 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 241 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
241 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 242 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
242 default_analogs[i][3], default_stick_mod[i], 0.5f); 243 default_analogs[i][3], default_stick_mod[i], 0.5f);
243 WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), 244 WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]),
244 Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); 245 Settings::values.debug_pad_analogs[i],
246 std::make_optional(default_param));
245 } 247 }
246} 248}
247 249
248void SdlConfig::SaveHidbusValues() { 250void SdlConfig::SaveHidbusValues() {
249 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 251 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
250 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); 252 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
251 WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, 253 WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs,
252 std::make_optional(default_param)); 254 std::make_optional(default_param));
253} 255}
254 256
255std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) { 257std::vector<Settings::BasicSetting*>& SdlConfig::FindRelevantList(Settings::Category category) {