summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/command_generator.cpp20
-rw-r--r--src/audio_core/effect_context.cpp4
-rw-r--r--src/audio_core/splitter_context.cpp2
-rw-r--r--src/audio_core/voice_context.cpp4
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/assert.cpp7
-rw-r--r--src/common/assert.h55
-rw-r--r--src/common/bounded_threadsafe_queue.h180
-rw-r--r--src/common/detached_tasks.cpp4
-rw-r--r--src/common/elf.h333
-rw-r--r--src/common/fs/path_util.cpp4
-rw-r--r--src/common/input.h1
-rw-r--r--src/common/settings.cpp3
-rw-r--r--src/common/settings.h5
-rw-r--r--src/common/string_util.cpp4
-rw-r--r--src/common/string_util.h2
-rw-r--r--src/core/CMakeLists.txt15
-rw-r--r--src/core/arm/arm_interface.cpp48
-rw-r--r--src/core/arm/arm_interface.h17
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp34
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp33
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp2
-rw-r--r--src/core/arm/symbols.cpp85
-rw-r--r--src/core/core.cpp35
-rw-r--r--src/core/core.h21
-rw-r--r--src/core/cpu_manager.cpp120
-rw-r--r--src/core/cpu_manager.h11
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/debugger/debugger.cpp310
-rw-r--r--src/core/debugger/debugger.h46
-rw-r--r--src/core/debugger/debugger_interface.h84
-rw-r--r--src/core/debugger/gdbstub.cpp620
-rw-r--r--src/core/debugger/gdbstub.h49
-rw-r--r--src/core/debugger/gdbstub_arch.cpp483
-rw-r--r--src/core/debugger/gdbstub_arch.h68
-rw-r--r--src/core/file_sys/content_archive.cpp2
-rw-r--r--src/core/file_sys/nca_patch.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp2
-rw-r--r--src/core/file_sys/vfs_real.cpp2
-rw-r--r--src/core/frontend/applets/controller.cpp2
-rw-r--r--src/core/hid/emulated_console.cpp15
-rw-r--r--src/core/hid/emulated_controller.cpp34
-rw-r--r--src/core/hid/hid_core.cpp4
-rw-r--r--src/core/hid/hid_types.h43
-rw-r--r--src/core/hid/input_converter.cpp4
-rw-r--r--src/core/hle/kernel/hle_ipc.h2
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h4
-rw-r--r--src/core/hle/kernel/k_address_space_info.cpp4
-rw-r--r--src/core/hle/kernel/k_auto_object.h11
-rw-r--r--src/core/hle/kernel/k_class_token.h1
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp28
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp2
-rw-r--r--src/core/hle/kernel/k_page_table.cpp127
-rw-r--r--src/core/hle/kernel/k_page_table.h5
-rw-r--r--src/core/hle/kernel/k_port.cpp2
-rw-r--r--src/core/hle/kernel/k_process.cpp6
-rw-r--r--src/core/hle/kernel/k_server_session.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.cpp6
-rw-r--r--src/core/hle/kernel/k_thread.h25
-rw-r--r--src/core/hle/kernel/kernel.cpp8
-rw-r--r--src/core/hle/kernel/svc.cpp12
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp4
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp4
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp752
-rw-r--r--src/core/hle/service/hid/controllers/npad.h75
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp6
-rw-r--r--src/core/hle/service/hid/errors.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp241
-rw-r--r--src/core/hle/service/hid/hid.h5
-rw-r--r--src/core/hle/service/hid/irs.cpp249
-rw-r--r--src/core/hle/service/hid/irs.h232
-rw-r--r--src/core/hle/service/jit/jit_context.cpp32
-rw-r--r--src/core/hle/service/ldr/ldr.cpp2
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp2
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp29
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp4
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h1
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp4
-rw-r--r--src/core/hle/service/time/time_manager.cpp10
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp9
-rw-r--r--src/core/loader/elf.cpp183
-rw-r--r--src/core/loader/nso.cpp9
-rw-r--r--src/core/memory.cpp13
-rw-r--r--src/core/memory.h11
-rw-r--r--src/core/tools/freezer.cpp1
-rw-r--r--src/input_common/CMakeLists.txt1
-rw-r--r--src/input_common/drivers/sdl_driver.cpp79
-rw-r--r--src/input_common/drivers/sdl_driver.h27
-rw-r--r--src/input_common/drivers/touch_screen.cpp89
-rw-r--r--src/input_common/drivers/touch_screen.h52
-rw-r--r--src/shader_recompiler/CMakeLists.txt3
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/opcodes.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp6
-rw-r--r--src/video_core/CMakeLists.txt9
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp2
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp8
-rw-r--r--src/video_core/command_classes/vic.cpp2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h14
-rw-r--r--src/video_core/engines/maxwell_dma.cpp13
-rw-r--r--src/video_core/gpu_thread.cpp5
-rw-r--r--src/video_core/gpu_thread.h6
-rw-r--r--src/video_core/macro/macro.cpp34
-rw-r--r--src/video_core/macro/macro.h3
-rw-r--r--src/video_core/macro/macro_interpreter.cpp1
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp26
-rw-r--r--src/video_core/memory_manager.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp4
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp38
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h12
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/renderer_opengl/util_shaders.cpp2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp53
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp24
-rw-r--r--src/video_core/shader_environment.cpp2
-rw-r--r--src/video_core/surface.cpp6
-rw-r--r--src/video_core/surface.h6
-rw-r--r--src/video_core/texture_cache/image_info.cpp2
-rw-r--r--src/video_core/texture_cache/image_view_info.cpp2
-rw-r--r--src/video_core/texture_cache/samples_helper.h4
-rw-r--r--src/video_core/texture_cache/texture_cache.h20
-rw-r--r--src/video_core/textures/decoders.cpp8
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp32
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp4
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp6
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp2
-rw-r--r--src/web_service/telemetry_json.cpp4
-rw-r--r--src/web_service/web_backend.cpp20
-rw-r--r--src/yuzu/CMakeLists.txt27
-rw-r--r--src/yuzu/about_dialog.cpp6
-rw-r--r--src/yuzu/aboutdialog.ui16
-rw-r--r--src/yuzu/applets/qt_controller.cpp2
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp29
-rw-r--r--src/yuzu/bootmanager.cpp81
-rw-r--r--src/yuzu/bootmanager.h30
-rw-r--r--src/yuzu/check_vulkan.cpp53
-rw-r--r--src/yuzu/check_vulkan.h6
-rw-r--r--src/yuzu/configuration/config.cpp57
-rw-r--r--src/yuzu/configuration/configure_debug.cpp12
-rw-r--r--src/yuzu/configuration/configure_debug.ui67
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp7
-rw-r--r--src/yuzu/configuration/configure_dialog.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp36
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui91
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp22
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp78
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui19
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp10
-rw-r--r--src/yuzu/configuration/configure_per_game.h4
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.cpp4
-rw-r--r--src/yuzu/configuration/configure_per_game_addons.h2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp8
-rw-r--r--src/yuzu/configuration/configure_system.cpp6
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp4
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.h2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp16
-rw-r--r--src/yuzu/debugger/wait_tree.h8
-rw-r--r--src/yuzu/game_list.cpp30
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/game_list_p.h8
-rw-r--r--src/yuzu/game_list_worker.cpp12
-rw-r--r--src/yuzu/game_list_worker.h8
-rw-r--r--src/yuzu/loading_screen.cpp2
-rw-r--r--src/yuzu/loading_screen.h3
-rw-r--r--src/yuzu/main.cpp143
-rw-r--r--src/yuzu/main.h3
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h7
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp12
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp4
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp9
202 files changed, 5010 insertions, 1649 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9182dbfd4..39d038493 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -65,6 +65,10 @@ if (MSVC)
65 /we4305 # 'context': truncation from 'type1' to 'type2' 65 /we4305 # 'context': truncation from 'type1' to 'type2'
66 /we4388 # 'expression': signed/unsigned mismatch 66 /we4388 # 'expression': signed/unsigned mismatch
67 /we4389 # 'operator': signed/unsigned mismatch 67 /we4389 # 'operator': signed/unsigned mismatch
68 /we4456 # Declaration of 'identifier' hides previous local declaration
69 /we4457 # Declaration of 'identifier' hides function parameter
70 /we4458 # Declaration of 'identifier' hides class member
71 /we4459 # Declaration of 'identifier' hides global declaration
68 /we4505 # 'function': unreferenced local function has been removed 72 /we4505 # 'function': unreferenced local function has been removed
69 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect 73 /we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
70 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'? 74 /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
@@ -92,6 +96,7 @@ else()
92 -Werror=missing-declarations 96 -Werror=missing-declarations
93 -Werror=missing-field-initializers 97 -Werror=missing-field-initializers
94 -Werror=reorder 98 -Werror=reorder
99 -Werror=shadow
95 -Werror=sign-compare 100 -Werror=sign-compare
96 -Werror=switch 101 -Werror=switch
97 -Werror=uninitialized 102 -Werror=uninitialized
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index e553b8203..89575a53e 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -49,9 +49,6 @@ if (NOT MSVC)
49 target_compile_options(audio_core PRIVATE 49 target_compile_options(audio_core PRIVATE
50 -Werror=conversion 50 -Werror=conversion
51 -Werror=ignored-qualifiers 51 -Werror=ignored-qualifiers
52 -Werror=shadow
53 -Werror=unused-parameter
54 -Werror=unused-variable
55 52
56 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 53 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
57 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 54 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index ae4efafb6..f97520820 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
129 return static_cast<s32>(rescaled_sample); 129 return static_cast<s32>(rescaled_sample);
130} 130}
131 131
132constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
134 134
135constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 135constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
136 1, 1, 1, 0, 0, 0, 0, 1, 1, 1}; 136 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
137 137
138constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 138constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
139 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; 139 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
140 140
141constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2, 141constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
142 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; 142 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
143 143
144template <std::size_t CHANNEL_COUNT> 144template <std::size_t CHANNEL_COUNT>
145void ApplyReverbGeneric( 145void ApplyReverbGeneric(
@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
429 in_params.node_id); 429 in_params.node_id);
430 break; 430 break;
431 default: 431 default:
432 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); 432 ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
433 } 433 }
434 } 434 }
435} 435}
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
1312 samples_to_read - samples_read, channel, temp_mix_offset); 1312 samples_to_read - samples_read, channel, temp_mix_offset);
1313 break; 1313 break;
1314 default: 1314 default:
1315 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); 1315 ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
1316 } 1316 }
1317 1317
1318 temp_mix_offset += samples_decoded; 1318 temp_mix_offset += samples_decoded;
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
index 51059580e..79bcd1192 100644
--- a/src/audio_core/effect_context.cpp
+++ b/src/audio_core/effect_context.cpp
@@ -50,7 +50,7 @@ EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
50 effects[i] = std::make_unique<EffectBiquadFilter>(); 50 effects[i] = std::make_unique<EffectBiquadFilter>();
51 break; 51 break;
52 default: 52 default:
53 UNREACHABLE_MSG("Unimplemented effect {}", effect); 53 ASSERT_MSG(false, "Unimplemented effect {}", effect);
54 effects[i] = std::make_unique<EffectStubbed>(); 54 effects[i] = std::make_unique<EffectStubbed>();
55 } 55 }
56 return GetInfo(i); 56 return GetInfo(i);
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
104 auto& params = GetParams(); 104 auto& params = GetParams();
105 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data()); 105 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
106 if (!ValidChannelCountForEffect(reverb_params->max_channels)) { 106 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
107 UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels); 107 ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
108 return; 108 return;
109 } 109 }
110 110
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
index 1751d0212..10646dc05 100644
--- a/src/audio_core/splitter_context.cpp
+++ b/src/audio_core/splitter_context.cpp
@@ -483,7 +483,7 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
483 // Add more work 483 // Add more work
484 index_stack.push(j); 484 index_stack.push(j);
485 } else if (node_state == NodeStates::State::InFound) { 485 } else if (node_state == NodeStates::State::InFound) {
486 UNREACHABLE_MSG("Node start marked as found"); 486 ASSERT_MSG(false, "Node start marked as found");
487 ResetState(); 487 ResetState();
488 return false; 488 return false;
489 } 489 }
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
index c8e4a6caf..f58a5c754 100644
--- a/src/audio_core/voice_context.cpp
+++ b/src/audio_core/voice_context.cpp
@@ -114,7 +114,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
114 in_params.current_playstate = ServerPlayState::Play; 114 in_params.current_playstate = ServerPlayState::Play;
115 break; 115 break;
116 default: 116 default:
117 UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state); 117 ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
118 break; 118 break;
119 } 119 }
120 120
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
410 return in_params.should_depop; 410 return in_params.should_depop;
411 } 411 }
412 default: 412 default:
413 UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate); 413 ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
414 } 414 }
415 415
416 return false; 416 return false;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index adf70eb8b..73bf626d4 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -58,6 +58,7 @@ add_library(common STATIC
58 div_ceil.h 58 div_ceil.h
59 dynamic_library.cpp 59 dynamic_library.cpp
60 dynamic_library.h 60 dynamic_library.h
61 elf.h
61 error.cpp 62 error.cpp
62 error.h 63 error.h
63 expected.h 64 expected.h
diff --git a/src/common/assert.cpp b/src/common/assert.cpp
index b44570528..6026b7dc2 100644
--- a/src/common/assert.cpp
+++ b/src/common/assert.cpp
@@ -6,8 +6,13 @@
6 6
7#include "common/settings.h" 7#include "common/settings.h"
8 8
9void assert_handle_failure() { 9void assert_fail_impl() {
10 if (Settings::values.use_debug_asserts) { 10 if (Settings::values.use_debug_asserts) {
11 Crash(); 11 Crash();
12 } 12 }
13} 13}
14
15[[noreturn]] void unreachable_impl() {
16 Crash();
17 throw std::runtime_error("Unreachable code");
18}
diff --git a/src/common/assert.h b/src/common/assert.h
index dbfd8abaf..8c927fcc0 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -9,44 +9,43 @@
9// Sometimes we want to try to continue even after hitting an assert. 9// Sometimes we want to try to continue even after hitting an assert.
10// However touching this file yields a global recompilation as this header is included almost 10// However touching this file yields a global recompilation as this header is included almost
11// everywhere. So let's just move the handling of the failed assert to a single cpp file. 11// everywhere. So let's just move the handling of the failed assert to a single cpp file.
12void assert_handle_failure();
13 12
14// For asserts we'd like to keep all the junk executed when an assert happens away from the 13void assert_fail_impl();
15// important code in the function. One way of doing this is to put all the relevant code inside a 14[[noreturn]] void unreachable_impl();
16// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to 15
17// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper 16#ifdef _MSC_VER
18// template that calls the lambda. This seems to generate an extra instruction at the call-site 17#define YUZU_NO_INLINE __declspec(noinline)
19// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good 18#else
20// enough for our purposes. 19#define YUZU_NO_INLINE __attribute__((noinline))
21template <typename Fn>
22#if defined(_MSC_VER)
23[[msvc::noinline]]
24#elif defined(__GNUC__)
25[[gnu::cold, gnu::noinline]]
26#endif 20#endif
27static void
28assert_noinline_call(const Fn& fn) {
29 fn();
30 assert_handle_failure();
31}
32 21
33#define ASSERT(_a_) \ 22#define ASSERT(_a_) \
34 do \ 23 ([&]() YUZU_NO_INLINE { \
35 if (!(_a_)) { \ 24 if (!(_a_)) [[unlikely]] { \
36 assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \ 25 LOG_CRITICAL(Debug, "Assertion Failed!"); \
26 assert_fail_impl(); \
37 } \ 27 } \
38 while (0) 28 }())
39 29
40#define ASSERT_MSG(_a_, ...) \ 30#define ASSERT_MSG(_a_, ...) \
41 do \ 31 ([&]() YUZU_NO_INLINE { \
42 if (!(_a_)) { \ 32 if (!(_a_)) [[unlikely]] { \
43 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \ 33 LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
34 assert_fail_impl(); \
44 } \ 35 } \
45 while (0) 36 }())
37
38#define UNREACHABLE() \
39 do { \
40 LOG_CRITICAL(Debug, "Unreachable code!"); \
41 unreachable_impl(); \
42 } while (0)
46 43
47#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
48#define UNREACHABLE_MSG(...) \ 44#define UNREACHABLE_MSG(...) \
49 assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); }) 45 do { \
46 LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
47 unreachable_impl(); \
48 } while (0)
50 49
51#ifdef _DEBUG 50#ifdef _DEBUG
52#define DEBUG_ASSERT(_a_) ASSERT(_a_) 51#define DEBUG_ASSERT(_a_) ASSERT(_a_)
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
new file mode 100644
index 000000000..e83064c7f
--- /dev/null
+++ b/src/common/bounded_threadsafe_queue.h
@@ -0,0 +1,180 @@
1// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
2// SPDX-License-Identifier: MIT
3#pragma once
4#ifdef _MSC_VER
5#pragma warning(push)
6#pragma warning(disable : 4324)
7#endif
8
9#include <atomic>
10#include <bit>
11#include <condition_variable>
12#include <memory>
13#include <mutex>
14#include <new>
15#include <stdexcept>
16#include <stop_token>
17#include <type_traits>
18#include <utility>
19
20namespace Common {
21namespace mpsc {
22#if defined(__cpp_lib_hardware_interference_size)
23constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
24#else
25constexpr size_t hardware_interference_size = 64;
26#endif
27
28template <typename T>
29using AlignedAllocator = std::allocator<T>;
30
31template <typename T>
32struct Slot {
33 ~Slot() noexcept {
34 if (turn.test()) {
35 destroy();
36 }
37 }
38
39 template <typename... Args>
40 void construct(Args&&... args) noexcept {
41 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
42 "T must be nothrow constructible with Args&&...");
43 std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...);
44 }
45
46 void destroy() noexcept {
47 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
48 std::destroy_at(reinterpret_cast<T*>(&storage));
49 }
50
51 T&& move() noexcept {
52 return reinterpret_cast<T&&>(storage);
53 }
54
55 // Align to avoid false sharing between adjacent slots
56 alignas(hardware_interference_size) std::atomic_flag turn{};
57 struct aligned_store {
58 struct type {
59 alignas(T) unsigned char data[sizeof(T)];
60 };
61 };
62 typename aligned_store::type storage;
63};
64
65template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
66class Queue {
67public:
68 explicit Queue(const size_t capacity, const Allocator& allocator = Allocator())
69 : allocator_(allocator) {
70 if (capacity < 1) {
71 throw std::invalid_argument("capacity < 1");
72 }
73 // Ensure that the queue length is an integer power of 2
74 // This is so that idx(i) can be a simple i & mask_ insted of i % capacity
75 // https://github.com/rigtorp/MPMCQueue/pull/36
76 if (!std::has_single_bit(capacity)) {
77 throw std::invalid_argument("capacity must be an integer power of 2");
78 }
79
80 mask_ = capacity - 1;
81
82 // Allocate one extra slot to prevent false sharing on the last slot
83 slots_ = allocator_.allocate(mask_ + 2);
84 // Allocators are not required to honor alignment for over-aligned types
85 // (see http://eel.is/c++draft/allocator.requirements#10) so we verify
86 // alignment here
87 if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) {
88 allocator_.deallocate(slots_, mask_ + 2);
89 throw std::bad_alloc();
90 }
91 for (size_t i = 0; i < mask_ + 1; ++i) {
92 std::construct_at(&slots_[i]);
93 }
94 static_assert(alignof(Slot<T>) == hardware_interference_size,
95 "Slot must be aligned to cache line boundary to prevent false sharing");
96 static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
97 "Slot size must be a multiple of cache line size to prevent "
98 "false sharing between adjacent slots");
99 static_assert(sizeof(Queue) % hardware_interference_size == 0,
100 "Queue size must be a multiple of cache line size to "
101 "prevent false sharing between adjacent queues");
102 }
103
104 ~Queue() noexcept {
105 for (size_t i = 0; i < mask_ + 1; ++i) {
106 slots_[i].~Slot();
107 }
108 allocator_.deallocate(slots_, mask_ + 2);
109 }
110
111 // non-copyable and non-movable
112 Queue(const Queue&) = delete;
113 Queue& operator=(const Queue&) = delete;
114
115 void Push(const T& v) noexcept {
116 static_assert(std::is_nothrow_copy_constructible_v<T>,
117 "T must be nothrow copy constructible");
118 emplace(v);
119 }
120
121 template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
122 void Push(P&& v) noexcept {
123 emplace(std::forward<P>(v));
124 }
125
126 void Pop(T& v, std::stop_token stop) noexcept {
127 auto const tail = tail_.fetch_add(1);
128 auto& slot = slots_[idx(tail)];
129 if (false == slot.turn.test()) {
130 std::unique_lock lock{cv_mutex};
131 cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
132 }
133 v = slot.move();
134 slot.destroy();
135 slot.turn.clear();
136 slot.turn.notify_one();
137 }
138
139private:
140 template <typename... Args>
141 void emplace(Args&&... args) noexcept {
142 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
143 "T must be nothrow constructible with Args&&...");
144 auto const head = head_.fetch_add(1);
145 auto& slot = slots_[idx(head)];
146 slot.turn.wait(true);
147 slot.construct(std::forward<Args>(args)...);
148 slot.turn.test_and_set();
149 cv.notify_one();
150 }
151
152 constexpr size_t idx(size_t i) const noexcept {
153 return i & mask_;
154 }
155
156 std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv;
157 std::mutex cv_mutex;
158 size_t mask_;
159 Slot<T>* slots_;
160 [[no_unique_address]] Allocator allocator_;
161
162 // Align to avoid false sharing between head_ and tail_
163 alignas(hardware_interference_size) std::atomic<size_t> head_{0};
164 alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
165
166 static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
167 "T must be nothrow copy or move assignable");
168
169 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
170};
171} // namespace mpsc
172
173template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>>
174using MPSCQueue = mpsc::Queue<T, Allocator>;
175
176} // namespace Common
177
178#ifdef _MSC_VER
179#pragma warning(pop)
180#endif
diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp
index c1362631e..ec31d0b88 100644
--- a/src/common/detached_tasks.cpp
+++ b/src/common/detached_tasks.cpp
@@ -33,9 +33,9 @@ void DetachedTasks::AddTask(std::function<void()> task) {
33 ++instance->count; 33 ++instance->count;
34 std::thread([task{std::move(task)}]() { 34 std::thread([task{std::move(task)}]() {
35 task(); 35 task();
36 std::unique_lock lock{instance->mutex}; 36 std::unique_lock thread_lock{instance->mutex};
37 --instance->count; 37 --instance->count;
38 std::notify_all_at_thread_exit(instance->cv, std::move(lock)); 38 std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
39 }).detach(); 39 }).detach();
40} 40}
41 41
diff --git a/src/common/elf.h b/src/common/elf.h
new file mode 100644
index 000000000..14a5e9597
--- /dev/null
+++ b/src/common/elf.h
@@ -0,0 +1,333 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common_types.h"
10
11namespace Common {
12namespace ELF {
13
14/* Type for a 16-bit quantity. */
15using Elf32_Half = u16;
16using Elf64_Half = u16;
17
18/* Types for signed and unsigned 32-bit quantities. */
19using Elf32_Word = u32;
20using Elf32_Sword = s32;
21using Elf64_Word = u32;
22using Elf64_Sword = s32;
23
24/* Types for signed and unsigned 64-bit quantities. */
25using Elf32_Xword = u64;
26using Elf32_Sxword = s64;
27using Elf64_Xword = u64;
28using Elf64_Sxword = s64;
29
30/* Type of addresses. */
31using Elf32_Addr = u32;
32using Elf64_Addr = u64;
33
34/* Type of file offsets. */
35using Elf32_Off = u32;
36using Elf64_Off = u64;
37
38/* Type for section indices, which are 16-bit quantities. */
39using Elf32_Section = u16;
40using Elf64_Section = u16;
41
42/* Type for version symbol information. */
43using Elf32_Versym = Elf32_Half;
44using Elf64_Versym = Elf64_Half;
45
46constexpr size_t ElfIdentSize = 16;
47
48/* The ELF file header. This appears at the start of every ELF file. */
49
50struct Elf32_Ehdr {
51 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
52 Elf32_Half e_type; /* Object file type */
53 Elf32_Half e_machine; /* Architecture */
54 Elf32_Word e_version; /* Object file version */
55 Elf32_Addr e_entry; /* Entry point virtual address */
56 Elf32_Off e_phoff; /* Program header table file offset */
57 Elf32_Off e_shoff; /* Section header table file offset */
58 Elf32_Word e_flags; /* Processor-specific flags */
59 Elf32_Half e_ehsize; /* ELF header size in bytes */
60 Elf32_Half e_phentsize; /* Program header table entry size */
61 Elf32_Half e_phnum; /* Program header table entry count */
62 Elf32_Half e_shentsize; /* Section header table entry size */
63 Elf32_Half e_shnum; /* Section header table entry count */
64 Elf32_Half e_shstrndx; /* Section header string table index */
65};
66
67struct Elf64_Ehdr {
68 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
69 Elf64_Half e_type; /* Object file type */
70 Elf64_Half e_machine; /* Architecture */
71 Elf64_Word e_version; /* Object file version */
72 Elf64_Addr e_entry; /* Entry point virtual address */
73 Elf64_Off e_phoff; /* Program header table file offset */
74 Elf64_Off e_shoff; /* Section header table file offset */
75 Elf64_Word e_flags; /* Processor-specific flags */
76 Elf64_Half e_ehsize; /* ELF header size in bytes */
77 Elf64_Half e_phentsize; /* Program header table entry size */
78 Elf64_Half e_phnum; /* Program header table entry count */
79 Elf64_Half e_shentsize; /* Section header table entry size */
80 Elf64_Half e_shnum; /* Section header table entry count */
81 Elf64_Half e_shstrndx; /* Section header string table index */
82};
83
84constexpr u8 ElfClass32 = 1; /* 32-bit objects */
85constexpr u8 ElfClass64 = 2; /* 64-bit objects */
86constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
87constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
88constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
89
90constexpr u16 ElfTypeNone = 0; /* No file type */
91constexpr u16 ElfTypeRel = 0; /* Relocatable file */
92constexpr u16 ElfTypeExec = 0; /* Executable file */
93constexpr u16 ElfTypeDyn = 0; /* Shared object file */
94
95constexpr u16 ElfMachineArm = 40; /* ARM */
96constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
97
98constexpr std::array<u8, ElfIdentSize> Elf32Ident{
99 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
100
101constexpr std::array<u8, ElfIdentSize> Elf64Ident{
102 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
103
104/* Section header. */
105
106struct Elf32_Shdr {
107 Elf32_Word sh_name; /* Section name (string tbl index) */
108 Elf32_Word sh_type; /* Section type */
109 Elf32_Word sh_flags; /* Section flags */
110 Elf32_Addr sh_addr; /* Section virtual addr at execution */
111 Elf32_Off sh_offset; /* Section file offset */
112 Elf32_Word sh_size; /* Section size in bytes */
113 Elf32_Word sh_link; /* Link to another section */
114 Elf32_Word sh_info; /* Additional section information */
115 Elf32_Word sh_addralign; /* Section alignment */
116 Elf32_Word sh_entsize; /* Entry size if section holds table */
117};
118
119struct Elf64_Shdr {
120 Elf64_Word sh_name; /* Section name (string tbl index) */
121 Elf64_Word sh_type; /* Section type */
122 Elf64_Xword sh_flags; /* Section flags */
123 Elf64_Addr sh_addr; /* Section virtual addr at execution */
124 Elf64_Off sh_offset; /* Section file offset */
125 Elf64_Xword sh_size; /* Section size in bytes */
126 Elf64_Word sh_link; /* Link to another section */
127 Elf64_Word sh_info; /* Additional section information */
128 Elf64_Xword sh_addralign; /* Section alignment */
129 Elf64_Xword sh_entsize; /* Entry size if section holds table */
130};
131
132constexpr u32 ElfShnUndef = 0; /* Undefined section */
133
134constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
135constexpr u32 ElfShtProgBits = 1; /* Program data */
136constexpr u32 ElfShtSymtab = 2; /* Symbol table */
137constexpr u32 ElfShtStrtab = 3; /* String table */
138constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
139constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
140constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
141constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
142constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
143
144/* Symbol table entry. */
145
146struct Elf32_Sym {
147 Elf32_Word st_name; /* Symbol name (string tbl index) */
148 Elf32_Addr st_value; /* Symbol value */
149 Elf32_Word st_size; /* Symbol size */
150 u8 st_info; /* Symbol type and binding */
151 u8 st_other; /* Symbol visibility */
152 Elf32_Section st_shndx; /* Section index */
153};
154
155struct Elf64_Sym {
156 Elf64_Word st_name; /* Symbol name (string tbl index) */
157 u8 st_info; /* Symbol type and binding */
158 u8 st_other; /* Symbol visibility */
159 Elf64_Section st_shndx; /* Section index */
160 Elf64_Addr st_value; /* Symbol value */
161 Elf64_Xword st_size; /* Symbol size */
162};
163
164/* How to extract and insert information held in the st_info field. */
165
166static inline u8 ElfStBind(u8 st_info) {
167 return st_info >> 4;
168}
169static inline u8 ElfStType(u8 st_info) {
170 return st_info & 0xf;
171}
172static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
173 return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
174}
175
176constexpr u8 ElfBindLocal = 0; /* Local symbol */
177constexpr u8 ElfBindGlobal = 1; /* Global symbol */
178constexpr u8 ElfBindWeak = 2; /* Weak symbol */
179
180constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
181constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
182constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
183
184static inline u8 ElfStVisibility(u8 st_other) {
185 return static_cast<u8>(st_other & 0x3);
186}
187
188constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
189constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
190constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
191constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
192
193/* Relocation table entry without addend (in section of type ShtRel). */
194
195struct Elf32_Rel {
196 Elf32_Addr r_offset; /* Address */
197 Elf32_Word r_info; /* Relocation type and symbol index */
198};
199
200/* Relocation table entry with addend (in section of type ShtRela). */
201
202struct Elf32_Rela {
203 Elf32_Addr r_offset; /* Address */
204 Elf32_Word r_info; /* Relocation type and symbol index */
205 Elf32_Sword r_addend; /* Addend */
206};
207
208struct Elf64_Rela {
209 Elf64_Addr r_offset; /* Address */
210 Elf64_Xword r_info; /* Relocation type and symbol index */
211 Elf64_Sxword r_addend; /* Addend */
212};
213
214/* How to extract and insert information held in the r_info field. */
215
216static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
217 return r_info >> 8;
218}
219static inline u8 Elf32RelType(Elf32_Word r_info) {
220 return static_cast<u8>(r_info & 0xff);
221}
222static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
223 return (sym_index << 8) + type;
224}
225static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
226 return static_cast<u32>(r_info >> 32);
227}
228static inline u32 Elf64RelType(Elf64_Xword r_info) {
229 return r_info & 0xffffffff;
230}
231static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
232 return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
233}
234
235constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
236constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
237constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
238constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
239
240constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
241constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
242constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
243constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
244
245/* Program segment header. */
246
247struct Elf32_Phdr {
248 Elf32_Word p_type; /* Segment type */
249 Elf32_Off p_offset; /* Segment file offset */
250 Elf32_Addr p_vaddr; /* Segment virtual address */
251 Elf32_Addr p_paddr; /* Segment physical address */
252 Elf32_Word p_filesz; /* Segment size in file */
253 Elf32_Word p_memsz; /* Segment size in memory */
254 Elf32_Word p_flags; /* Segment flags */
255 Elf32_Word p_align; /* Segment alignment */
256};
257
258struct Elf64_Phdr {
259 Elf64_Word p_type; /* Segment type */
260 Elf64_Word p_flags; /* Segment flags */
261 Elf64_Off p_offset; /* Segment file offset */
262 Elf64_Addr p_vaddr; /* Segment virtual address */
263 Elf64_Addr p_paddr; /* Segment physical address */
264 Elf64_Xword p_filesz; /* Segment size in file */
265 Elf64_Xword p_memsz; /* Segment size in memory */
266 Elf64_Xword p_align; /* Segment alignment */
267};
268
269/* Legal values for p_type (segment type). */
270
271constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
272constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
273constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
274constexpr u32 ElfPtInterp = 3; /* Program interpreter */
275constexpr u32 ElfPtNote = 4; /* Auxiliary information */
276constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
277constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
278
279/* Legal values for p_flags (segment flags). */
280
281constexpr u32 ElfPfExec = 0; /* Segment is executable */
282constexpr u32 ElfPfWrite = 1; /* Segment is writable */
283constexpr u32 ElfPfRead = 2; /* Segment is readable */
284
285/* Dynamic section entry. */
286
287struct Elf32_Dyn {
288 Elf32_Sword d_tag; /* Dynamic entry type */
289 union {
290 Elf32_Word d_val; /* Integer value */
291 Elf32_Addr d_ptr; /* Address value */
292 } d_un;
293};
294
295struct Elf64_Dyn {
296 Elf64_Sxword d_tag; /* Dynamic entry type */
297 union {
298 Elf64_Xword d_val; /* Integer value */
299 Elf64_Addr d_ptr; /* Address value */
300 } d_un;
301};
302
303/* Legal values for d_tag (dynamic entry type). */
304
305constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
306constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
307constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
308constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
309constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
310constexpr u32 ElfDtStrtab = 5; /* Address of string table */
311constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
312constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
313constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
314constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
315constexpr u32 ElfDtStrsz = 10; /* Size of string table */
316constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
317constexpr u32 ElfDtInit = 12; /* Address of init function */
318constexpr u32 ElfDtFini = 13; /* Address of termination function */
319constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
320constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
321constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
322constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
323constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
324constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
325constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
326constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
327constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
328constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
329constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
330constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
331
332} // namespace ELF
333} // namespace Common
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 62318e70c..1074f2421 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -232,9 +232,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) {
232fs::path GetExeDirectory() { 232fs::path GetExeDirectory() {
233 wchar_t exe_path[MAX_PATH]; 233 wchar_t exe_path[MAX_PATH];
234 234
235 GetModuleFileNameW(nullptr, exe_path, MAX_PATH); 235 if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
236
237 if (!exe_path) {
238 LOG_ERROR(Common_Filesystem, 236 LOG_ERROR(Common_Filesystem,
239 "Failed to get the path to the executable of the current process"); 237 "Failed to get the path to the executable of the current process");
240 } 238 }
diff --git a/src/common/input.h b/src/common/input.h
index 54fcb24b0..bb42aaacc 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -72,6 +72,7 @@ enum class PollingError {
72enum class VibrationAmplificationType { 72enum class VibrationAmplificationType {
73 Linear, 73 Linear,
74 Exponential, 74 Exponential,
75 Test,
75}; 76};
76 77
77// Analog properties for calibration 78// Analog properties for calibration
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 9a9c74a70..751549583 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -70,6 +70,7 @@ void LogSettings() {
70 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); 70 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
71 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); 71 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
72 log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); 72 log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
73 log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
73 log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); 74 log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
74 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); 75 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
75 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); 76 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
@@ -146,7 +147,7 @@ void UpdateRescalingInfo() {
146 info.down_shift = 0; 147 info.down_shift = 0;
147 break; 148 break;
148 default: 149 default:
149 UNREACHABLE(); 150 ASSERT(false);
150 info.up_scale = 1; 151 info.up_scale = 1;
151 info.down_shift = 0; 152 info.down_shift = 0;
152 } 153 }
diff --git a/src/common/settings.h b/src/common/settings.h
index 5b34169a8..a507744a2 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -496,7 +496,7 @@ struct Values {
496 496
497 // Renderer 497 // Renderer
498 RangedSetting<RendererBackend> renderer_backend{ 498 RangedSetting<RendererBackend> renderer_backend{
499 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 499 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
500 BasicSetting<bool> renderer_debug{false, "debug"}; 500 BasicSetting<bool> renderer_debug{false, "debug"};
501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -601,11 +601,12 @@ struct Values {
601 // Debugging 601 // Debugging
602 bool record_frame_times; 602 bool record_frame_times;
603 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; 603 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
604 BasicSetting<u16> gdbstub_port{0, "gdbstub_port"}; 604 BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
605 BasicSetting<std::string> program_args{std::string(), "program_args"}; 605 BasicSetting<std::string> program_args{std::string(), "program_args"};
606 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 606 BasicSetting<bool> dump_exefs{false, "dump_exefs"};
607 BasicSetting<bool> dump_nso{false, "dump_nso"}; 607 BasicSetting<bool> dump_nso{false, "dump_nso"};
608 BasicSetting<bool> dump_shaders{false, "dump_shaders"}; 608 BasicSetting<bool> dump_shaders{false, "dump_shaders"};
609 BasicSetting<bool> dump_macros{false, "dump_macros"};
609 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"}; 610 BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
610 BasicSetting<bool> reporting_services{false, "reporting_services"}; 611 BasicSetting<bool> reporting_services{false, "reporting_services"};
611 BasicSetting<bool> quest_flag{false, "quest_flag"}; 612 BasicSetting<bool> quest_flag{false, "quest_flag"};
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 703aa5db8..7a495bc79 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -178,6 +178,10 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
178 178
179#endif 179#endif
180 180
181std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
182 return std::u16string(reinterpret_cast<const char16_t*>(input), length);
183}
184
181std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) { 185std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
182 std::size_t len = 0; 186 std::size_t len = 0;
183 while (len < buffer.length() && len < max_len && buffer[len] != '\0') { 187 while (len < buffer.length() && len < max_len && buffer[len] != '\0') {
diff --git a/src/common/string_util.h b/src/common/string_util.h
index a33830aec..ce18a33cf 100644
--- a/src/common/string_util.h
+++ b/src/common/string_util.h
@@ -44,6 +44,8 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
44 44
45#endif 45#endif
46 46
47[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
48
47/** 49/**
48 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string 50 * Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
49 * `other` for equality. 51 * `other` for equality.
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 62230bae0..670410e75 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -36,6 +36,13 @@ add_library(core STATIC
36 crypto/ctr_encryption_layer.h 36 crypto/ctr_encryption_layer.h
37 crypto/xts_encryption_layer.cpp 37 crypto/xts_encryption_layer.cpp
38 crypto/xts_encryption_layer.h 38 crypto/xts_encryption_layer.h
39 debugger/debugger_interface.h
40 debugger/debugger.cpp
41 debugger/debugger.h
42 debugger/gdbstub_arch.cpp
43 debugger/gdbstub_arch.h
44 debugger/gdbstub.cpp
45 debugger/gdbstub.h
39 device_memory.cpp 46 device_memory.cpp
40 device_memory.h 47 device_memory.h
41 file_sys/bis_factory.cpp 48 file_sys/bis_factory.cpp
@@ -736,16 +743,11 @@ if (MSVC)
736 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 743 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
737 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch 744 /we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
738 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 745 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
739 /we4456 # Declaration of 'identifier' hides previous local declaration
740 /we4457 # Declaration of 'identifier' hides function parameter
741 /we4458 # Declaration of 'identifier' hides class member
742 /we4459 # Declaration of 'identifier' hides global declaration
743 ) 746 )
744else() 747else()
745 target_compile_options(core PRIVATE 748 target_compile_options(core PRIVATE
746 -Werror=conversion 749 -Werror=conversion
747 -Werror=ignored-qualifiers 750 -Werror=ignored-qualifiers
748 -Werror=shadow
749 751
750 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 752 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
751 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 753 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
@@ -761,6 +763,9 @@ create_target_directory_groups(core)
761 763
762target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 764target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
763target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) 765target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
766if (MINGW)
767 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
768endif()
764 769
765if (ENABLE_WEB_SERVICE) 770if (ENABLE_WEB_SERVICE)
766 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 771 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index c347e7ea7..9a285dfc6 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -9,7 +9,9 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/arm/symbols.h" 10#include "core/arm/symbols.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/debugger/debugger.h"
12#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/hle/kernel/svc.h"
13#include "core/loader/loader.h" 15#include "core/loader/loader.h"
14#include "core/memory.h" 16#include "core/memory.h"
15 17
@@ -88,4 +90,50 @@ void ARM_Interface::LogBacktrace() const {
88 } 90 }
89} 91}
90 92
93void ARM_Interface::Run() {
94 using Kernel::StepState;
95 using Kernel::SuspendType;
96
97 while (true) {
98 Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
99 Dynarmic::HaltReason hr{};
100
101 // Notify the debugger and go to sleep if a step was performed
102 // and this thread has been scheduled again.
103 if (current_thread->GetStepState() == StepState::StepPerformed) {
104 system.GetDebugger().NotifyThreadStopped(current_thread);
105 current_thread->RequestSuspend(SuspendType::Debug);
106 break;
107 }
108
109 // Otherwise, run the thread.
110 system.EnterDynarmicProfile();
111 if (current_thread->GetStepState() == StepState::StepPending) {
112 hr = StepJit();
113
114 if (Has(hr, step_thread)) {
115 current_thread->SetStepState(StepState::StepPerformed);
116 }
117 } else {
118 hr = RunJit();
119 }
120 system.ExitDynarmicProfile();
121
122 // Notify the debugger and go to sleep if a breakpoint was hit.
123 if (Has(hr, breakpoint)) {
124 system.GetDebugger().NotifyThreadStopped(current_thread);
125 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
126 break;
127 }
128
129 // Handle syscalls and scheduling (this may change the current thread)
130 if (Has(hr, svc_call)) {
131 Kernel::Svc::Call(system, GetSvcNumber());
132 }
133 if (Has(hr, break_loop) || !uses_wall_clock) {
134 break;
135 }
136 }
137}
138
91} // namespace Core 139} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8ce973a77..66f6107e9 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
10#include <dynarmic/interface/halt_reason.h>
11
9#include "common/common_funcs.h" 12#include "common/common_funcs.h"
10#include "common/common_types.h" 13#include "common/common_types.h"
11#include "core/hardware_properties.h" 14#include "core/hardware_properties.h"
@@ -64,10 +67,7 @@ public:
64 static_assert(sizeof(ThreadContext64) == 0x320); 67 static_assert(sizeof(ThreadContext64) == 0x320);
65 68
66 /// Runs the CPU until an event happens 69 /// Runs the CPU until an event happens
67 virtual void Run() = 0; 70 void Run();
68
69 /// Step CPU by one instruction
70 virtual void Step() = 0;
71 71
72 /// Clear all instruction cache 72 /// Clear all instruction cache
73 virtual void ClearInstructionCache() = 0; 73 virtual void ClearInstructionCache() = 0;
@@ -194,6 +194,11 @@ public:
194 194
195 void LogBacktrace() const; 195 void LogBacktrace() const;
196 196
197 static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
198 static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
199 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
200 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
201
197protected: 202protected:
198 /// System context that this ARM interface is running under. 203 /// System context that this ARM interface is running under.
199 System& system; 204 System& system;
@@ -201,6 +206,10 @@ protected:
201 bool uses_wall_clock; 206 bool uses_wall_clock;
202 207
203 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); 208 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
209
210 virtual Dynarmic::HaltReason RunJit() = 0;
211 virtual Dynarmic::HaltReason StepJit() = 0;
212 virtual u32 GetSvcNumber() const = 0;
204}; 213};
205 214
206} // namespace Core 215} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 781a77f6f..7c82d0b96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -17,6 +17,8 @@
17#include "core/arm/dynarmic/arm_exclusive_monitor.h" 17#include "core/arm/dynarmic/arm_exclusive_monitor.h"
18#include "core/core.h" 18#include "core/core.h"
19#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/debugger/debugger.h"
21#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/svc.h" 22#include "core/hle/kernel/svc.h"
21#include "core/memory.h" 23#include "core/memory.h"
22 24
@@ -24,9 +26,6 @@ namespace Core {
24 26
25using namespace Common::Literals; 27using namespace Common::Literals;
26 28
27constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
28constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
29
30class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 29class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
31public: 30public:
32 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 31 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -78,16 +77,21 @@ public:
78 } 77 }
79 78
80 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 79 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
80 if (parent.system.DebuggerEnabled()) {
81 parent.jit.load()->Regs()[15] = pc;
82 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
83 return;
84 }
85
81 parent.LogBacktrace(); 86 parent.LogBacktrace();
82 LOG_CRITICAL(Core_ARM, 87 LOG_CRITICAL(Core_ARM,
83 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 88 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
84 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 89 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
85 UNIMPLEMENTED();
86 } 90 }
87 91
88 void CallSVC(u32 swi) override { 92 void CallSVC(u32 swi) override {
89 parent.svc_swi = swi; 93 parent.svc_swi = swi;
90 parent.jit.load()->HaltExecution(svc_call); 94 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
91 } 95 }
92 96
93 void AddTicks(u64 ticks) override { 97 void AddTicks(u64 ticks) override {
@@ -232,20 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
232 return std::make_unique<Dynarmic::A32::Jit>(config); 236 return std::make_unique<Dynarmic::A32::Jit>(config);
233} 237}
234 238
235void ARM_Dynarmic_32::Run() { 239Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
236 while (true) { 240 return jit.load()->Run();
237 const auto hr = jit.load()->Run(); 241}
238 if (Has(hr, svc_call)) { 242
239 Kernel::Svc::Call(system, svc_swi); 243Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
240 } 244 return jit.load()->Step();
241 if (Has(hr, break_loop) || !uses_wall_clock) {
242 break;
243 }
244 }
245} 245}
246 246
247void ARM_Dynarmic_32::Step() { 247u32 ARM_Dynarmic_32::GetSvcNumber() const {
248 jit.load()->Step(); 248 return svc_swi;
249} 249}
250 250
251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index abfe76644..5b1d60005 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -41,8 +41,6 @@ public:
41 void SetVectorReg(int index, u128 value) override; 41 void SetVectorReg(int index, u128 value) override;
42 u32 GetPSTATE() const override; 42 u32 GetPSTATE() const override;
43 void SetPSTATE(u32 pstate) override; 43 void SetPSTATE(u32 pstate) override;
44 void Run() override;
45 void Step() override;
46 VAddr GetTlsAddress() const override; 44 VAddr GetTlsAddress() const override;
47 void SetTlsAddress(VAddr address) override; 45 void SetTlsAddress(VAddr address) override;
48 void SetTPIDR_EL0(u64 value) override; 46 void SetTPIDR_EL0(u64 value) override;
@@ -70,6 +68,11 @@ public:
70 68
71 std::vector<BacktraceEntry> GetBacktrace() const override; 69 std::vector<BacktraceEntry> GetBacktrace() const override;
72 70
71protected:
72 Dynarmic::HaltReason RunJit() override;
73 Dynarmic::HaltReason StepJit() override;
74 u32 GetSvcNumber() const override;
75
73private: 76private:
74 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 77 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
75 78
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1b1334598..d4c67eafd 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -15,6 +15,7 @@
15#include "core/arm/dynarmic/arm_exclusive_monitor.h" 15#include "core/arm/dynarmic/arm_exclusive_monitor.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
18#include "core/hardware_properties.h" 19#include "core/hardware_properties.h"
19#include "core/hle/kernel/k_process.h" 20#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/svc.h" 21#include "core/hle/kernel/svc.h"
@@ -25,9 +26,6 @@ namespace Core {
25using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
26using namespace Common::Literals; 27using namespace Common::Literals;
27 28
28constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
29constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
30
31class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
32public: 30public:
33 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -119,8 +117,13 @@ public:
119 case Dynarmic::A64::Exception::SendEventLocal: 117 case Dynarmic::A64::Exception::SendEventLocal:
120 case Dynarmic::A64::Exception::Yield: 118 case Dynarmic::A64::Exception::Yield:
121 return; 119 return;
122 case Dynarmic::A64::Exception::Breakpoint:
123 default: 120 default:
121 if (parent.system.DebuggerEnabled()) {
122 parent.jit.load()->SetPC(pc);
123 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
124 return;
125 }
126
124 parent.LogBacktrace(); 127 parent.LogBacktrace();
125 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 128 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
126 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 129 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
@@ -129,7 +132,7 @@ public:
129 132
130 void CallSVC(u32 swi) override { 133 void CallSVC(u32 swi) override {
131 parent.svc_swi = swi; 134 parent.svc_swi = swi;
132 parent.jit.load()->HaltExecution(svc_call); 135 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
133 } 136 }
134 137
135 void AddTicks(u64 ticks) override { 138 void AddTicks(u64 ticks) override {
@@ -293,20 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
293 return std::make_shared<Dynarmic::A64::Jit>(config); 296 return std::make_shared<Dynarmic::A64::Jit>(config);
294} 297}
295 298
296void ARM_Dynarmic_64::Run() { 299Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
297 while (true) { 300 return jit.load()->Run();
298 const auto hr = jit.load()->Run(); 301}
299 if (Has(hr, svc_call)) { 302
300 Kernel::Svc::Call(system, svc_swi); 303Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
301 } 304 return jit.load()->Step();
302 if (Has(hr, break_loop) || !uses_wall_clock) {
303 break;
304 }
305 }
306} 305}
307 306
308void ARM_Dynarmic_64::Step() { 307u32 ARM_Dynarmic_64::GetSvcNumber() const {
309 jit.load()->Step(); 308 return svc_swi;
310} 309}
311 310
312ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 311ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 01a7e4dad..abfbc3c3f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -39,8 +39,6 @@ public:
39 void SetVectorReg(int index, u128 value) override; 39 void SetVectorReg(int index, u128 value) override;
40 u32 GetPSTATE() const override; 40 u32 GetPSTATE() const override;
41 void SetPSTATE(u32 pstate) override; 41 void SetPSTATE(u32 pstate) override;
42 void Run() override;
43 void Step() override;
44 VAddr GetTlsAddress() const override; 42 VAddr GetTlsAddress() const override;
45 void SetTlsAddress(VAddr address) override; 43 void SetTlsAddress(VAddr address) override;
46 void SetTPIDR_EL0(u64 value) override; 44 void SetTPIDR_EL0(u64 value) override;
@@ -64,6 +62,11 @@ public:
64 62
65 std::vector<BacktraceEntry> GetBacktrace() const override; 63 std::vector<BacktraceEntry> GetBacktrace() const override;
66 64
65protected:
66 Dynarmic::HaltReason RunJit() override;
67 Dynarmic::HaltReason StepJit() override;
68 u32 GetSvcNumber() const override;
69
67private: 70private:
68 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 71 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
69 std::size_t address_space_bits) const; 72 std::size_t address_space_bits) const;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index a043e6735..6aae79c48 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -20,7 +20,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
20 } 20 }
21 template <typename FormatContext> 21 template <typename FormatContext>
22 auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) { 22 auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
23 return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg)); 23 return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
24 } 24 }
25}; 25};
26 26
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
index 4aa1a1ee1..0259c7ea2 100644
--- a/src/core/arm/symbols.cpp
+++ b/src/core/arm/symbols.cpp
@@ -3,73 +3,14 @@
3 3
4#include "common/bit_field.h" 4#include "common/bit_field.h"
5#include "common/common_funcs.h" 5#include "common/common_funcs.h"
6#include "common/elf.h"
6#include "core/arm/symbols.h" 7#include "core/arm/symbols.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
9 10
10namespace Core { 11using namespace Common::ELF;
11namespace {
12
13constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
14constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
15constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
16constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
17
18enum class ELFSymbolType : u8 {
19 None = 0,
20 Object = 1,
21 Function = 2,
22 Section = 3,
23 File = 4,
24 Common = 5,
25 TLS = 6,
26};
27
28enum class ELFSymbolBinding : u8 {
29 Local = 0,
30 Global = 1,
31 Weak = 2,
32};
33
34enum class ELFSymbolVisibility : u8 {
35 Default = 0,
36 Internal = 1,
37 Hidden = 2,
38 Protected = 3,
39};
40
41struct ELF64Symbol {
42 u32 name_index;
43 union {
44 u8 info;
45
46 BitField<0, 4, ELFSymbolType> type;
47 BitField<4, 4, ELFSymbolBinding> binding;
48 };
49 ELFSymbolVisibility visibility;
50 u16 sh_index;
51 u64 value;
52 u64 size;
53};
54static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
55
56struct ELF32Symbol {
57 u32 name_index;
58 u32 value;
59 u32 size;
60 union {
61 u8 info;
62
63 BitField<0, 4, ELFSymbolType> type;
64 BitField<4, 4, ELFSymbolBinding> binding;
65 };
66 ELFSymbolVisibility visibility;
67 u16 sh_index;
68};
69static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
70
71} // Anonymous namespace
72 12
13namespace Core {
73namespace Symbols { 14namespace Symbols {
74 15
75template <typename Word, typename ELFSymbol, typename ByteReader> 16template <typename Word, typename ELFSymbol, typename ByteReader>
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
110 const Word value = ReadWord(dynamic_index + sizeof(Word)); 51 const Word value = ReadWord(dynamic_index + sizeof(Word));
111 dynamic_index += 2 * sizeof(Word); 52 dynamic_index += 2 * sizeof(Word);
112 53
113 if (tag == ELF_DYNAMIC_TAG_NULL) { 54 if (tag == ElfDtNull) {
114 break; 55 break;
115 } 56 }
116 57
117 if (tag == ELF_DYNAMIC_TAG_STRTAB) { 58 if (tag == ElfDtStrtab) {
118 string_table_offset = value; 59 string_table_offset = value;
119 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { 60 } else if (tag == ElfDtSymtab) {
120 symbol_table_offset = value; 61 symbol_table_offset = value;
121 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { 62 } else if (tag == ElfDtSyment) {
122 symbol_entry_size = value; 63 symbol_entry_size = value;
123 } 64 }
124 } 65 }
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
134 ELFSymbol symbol{}; 75 ELFSymbol symbol{};
135 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); 76 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
136 77
137 VAddr string_offset = string_table_offset + symbol.name_index; 78 VAddr string_offset = string_table_offset + symbol.st_name;
138 std::string name; 79 std::string name;
139 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { 80 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
140 name += static_cast<char>(c); 81 name += static_cast<char>(c);
141 } 82 }
142 83
143 symbol_index += symbol_entry_size; 84 symbol_index += symbol_entry_size;
144 out[name] = std::make_pair(symbol.value, symbol.size); 85 out[name] = std::make_pair(symbol.st_value, symbol.st_size);
145 } 86 }
146 87
147 return out; 88 return out;
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; 93 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
153 94
154 if (is_64) { 95 if (is_64) {
155 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 96 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
156 } else { 97 } else {
157 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 98 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
158 } 99 }
159} 100}
160 101
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
164 }}; 105 }};
165 106
166 if (is_64) { 107 if (is_64) {
167 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 108 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
168 } else { 109 } else {
169 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 110 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
170 } 111 }
171} 112}
172 113
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8a887904d..954136adb 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
17#include "core/core.h" 17#include "core/core.h"
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/cpu_manager.h" 19#include "core/cpu_manager.h"
20#include "core/debugger/debugger.h"
20#include "core/device_memory.h" 21#include "core/device_memory.h"
21#include "core/file_sys/bis_factory.h" 22#include "core/file_sys/bis_factory.h"
22#include "core/file_sys/mode.h" 23#include "core/file_sys/mode.h"
@@ -171,6 +172,10 @@ struct System::Impl {
171 } 172 }
172 } 173 }
173 174
175 void InitializeDebugger(System& system, u16 port) {
176 debugger = std::make_unique<Debugger>(system, port);
177 }
178
174 SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 179 SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
175 LOG_DEBUG(Core, "initialized OK"); 180 LOG_DEBUG(Core, "initialized OK");
176 181
@@ -329,6 +334,7 @@ struct System::Impl {
329 gpu_core->NotifyShutdown(); 334 gpu_core->NotifyShutdown();
330 } 335 }
331 336
337 debugger.reset();
332 services.reset(); 338 services.reset();
333 service_manager.reset(); 339 service_manager.reset();
334 cheat_engine.reset(); 340 cheat_engine.reset();
@@ -436,6 +442,9 @@ struct System::Impl {
436 /// Network instance 442 /// Network instance
437 Network::NetworkInstance network_instance; 443 Network::NetworkInstance network_instance;
438 444
445 /// Debugger
446 std::unique_ptr<Core::Debugger> debugger;
447
439 SystemResultStatus status = SystemResultStatus::Success; 448 SystemResultStatus status = SystemResultStatus::Success;
440 std::string status_details = ""; 449 std::string status_details = "";
441 450
@@ -472,10 +481,6 @@ SystemResultStatus System::Pause() {
472 return impl->Pause(); 481 return impl->Pause();
473} 482}
474 483
475SystemResultStatus System::SingleStep() {
476 return SystemResultStatus::Success;
477}
478
479void System::InvalidateCpuInstructionCaches() { 484void System::InvalidateCpuInstructionCaches() {
480 impl->kernel.InvalidateAllInstructionCaches(); 485 impl->kernel.InvalidateAllInstructionCaches();
481} 486}
@@ -488,6 +493,12 @@ void System::Shutdown() {
488 impl->Shutdown(); 493 impl->Shutdown();
489} 494}
490 495
496void System::DetachDebugger() {
497 if (impl->debugger) {
498 impl->debugger->NotifyShutdown();
499 }
500}
501
491std::unique_lock<std::mutex> System::StallCPU() { 502std::unique_lock<std::mutex> System::StallCPU() {
492 return impl->StallCPU(); 503 return impl->StallCPU();
493} 504}
@@ -496,6 +507,10 @@ void System::UnstallCPU() {
496 impl->UnstallCPU(); 507 impl->UnstallCPU();
497} 508}
498 509
510void System::InitializeDebugger() {
511 impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
512}
513
499SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 514SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
500 u64 program_id, std::size_t program_index) { 515 u64 program_id, std::size_t program_index) {
501 return impl->Load(*this, emu_window, filepath, program_id, program_index); 516 return impl->Load(*this, emu_window, filepath, program_id, program_index);
@@ -809,6 +824,18 @@ bool System::IsMulticore() const {
809 return impl->is_multicore; 824 return impl->is_multicore;
810} 825}
811 826
827bool System::DebuggerEnabled() const {
828 return Settings::values.use_gdbstub.GetValue();
829}
830
831Core::Debugger& System::GetDebugger() {
832 return *impl->debugger;
833}
834
835const Core::Debugger& System::GetDebugger() const {
836 return *impl->debugger;
837}
838
812void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { 839void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
813 impl->execute_program_callback = std::move(callback); 840 impl->execute_program_callback = std::move(callback);
814} 841}
diff --git a/src/core/core.h b/src/core/core.h
index 4a0c7dc84..5c367349e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -97,6 +97,7 @@ namespace Core {
97 97
98class ARM_Interface; 98class ARM_Interface;
99class CpuManager; 99class CpuManager;
100class Debugger;
100class DeviceMemory; 101class DeviceMemory;
101class ExclusiveMonitor; 102class ExclusiveMonitor;
102class SpeedLimiter; 103class SpeedLimiter;
@@ -148,12 +149,6 @@ public:
148 [[nodiscard]] SystemResultStatus Pause(); 149 [[nodiscard]] SystemResultStatus Pause();
149 150
150 /** 151 /**
151 * Step the CPU one instruction
152 * @return Result status, indicating whether or not the operation succeeded.
153 */
154 [[nodiscard]] SystemResultStatus SingleStep();
155
156 /**
157 * Invalidate the CPU instruction caches 152 * Invalidate the CPU instruction caches
158 * This function should only be used by GDB Stub to support breakpoints, memory updates and 153 * This function should only be used by GDB Stub to support breakpoints, memory updates and
159 * step/continue commands. 154 * step/continue commands.
@@ -165,10 +160,18 @@ public:
165 /// Shutdown the emulated system. 160 /// Shutdown the emulated system.
166 void Shutdown(); 161 void Shutdown();
167 162
163 /// Forcibly detach the debugger if it is running.
164 void DetachDebugger();
165
168 std::unique_lock<std::mutex> StallCPU(); 166 std::unique_lock<std::mutex> StallCPU();
169 void UnstallCPU(); 167 void UnstallCPU();
170 168
171 /** 169 /**
170 * Initialize the debugger.
171 */
172 void InitializeDebugger();
173
174 /**
172 * Load an executable application. 175 * Load an executable application.
173 * @param emu_window Reference to the host-system window used for video output and keyboard 176 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 177 * input.
@@ -354,6 +357,9 @@ public:
354 [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); 357 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
355 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; 358 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
356 359
360 [[nodiscard]] Core::Debugger& GetDebugger();
361 [[nodiscard]] const Core::Debugger& GetDebugger() const;
362
357 void SetExitLock(bool locked); 363 void SetExitLock(bool locked);
358 [[nodiscard]] bool GetExitLock() const; 364 [[nodiscard]] bool GetExitLock() const;
359 365
@@ -375,6 +381,9 @@ public:
375 /// Tells if system is running on multicore. 381 /// Tells if system is running on multicore.
376 [[nodiscard]] bool IsMulticore() const; 382 [[nodiscard]] bool IsMulticore() const;
377 383
384 /// Tells if the system debugger is enabled.
385 [[nodiscard]] bool DebuggerEnabled() const;
386
378 /// Type used for the frontend to designate a callback for System to re-launch the application 387 /// Type used for the frontend to designate a callback for System to re-launch the application
379 /// using a specified program index. 388 /// using a specified program index.
380 using ExecuteProgramCallback = std::function<void(std::size_t)>; 389 using ExecuteProgramCallback = std::function<void(std::size_t)>;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 09d9c5163..132fe5b60 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -16,7 +16,8 @@
16 16
17namespace Core { 17namespace Core {
18 18
19CpuManager::CpuManager(System& system_) : system{system_} {} 19CpuManager::CpuManager(System& system_)
20 : pause_barrier{std::make_unique<Common::Barrier>(1)}, system{system_} {}
20CpuManager::~CpuManager() = default; 21CpuManager::~CpuManager() = default;
21 22
22void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, 23void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
@@ -30,8 +31,10 @@ void CpuManager::Initialize() {
30 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 31 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
31 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); 32 core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
32 } 33 }
34 pause_barrier = std::make_unique<Common::Barrier>(Core::Hardware::NUM_CPU_CORES + 1);
33 } else { 35 } else {
34 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); 36 core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
37 pause_barrier = std::make_unique<Common::Barrier>(2);
35 } 38 }
36} 39}
37 40
@@ -110,12 +113,10 @@ void CpuManager::MultiCoreRunGuestLoop() {
110 113
111 while (true) { 114 while (true) {
112 auto* physical_core = &kernel.CurrentPhysicalCore(); 115 auto* physical_core = &kernel.CurrentPhysicalCore();
113 system.EnterDynarmicProfile();
114 while (!physical_core->IsInterrupted()) { 116 while (!physical_core->IsInterrupted()) {
115 physical_core->Run(); 117 physical_core->Run();
116 physical_core = &kernel.CurrentPhysicalCore(); 118 physical_core = &kernel.CurrentPhysicalCore();
117 } 119 }
118 system.ExitDynarmicProfile();
119 { 120 {
120 Kernel::KScopedDisableDispatch dd(kernel); 121 Kernel::KScopedDisableDispatch dd(kernel);
121 physical_core->ArmInterface().ClearExclusiveState(); 122 physical_core->ArmInterface().ClearExclusiveState();
@@ -138,51 +139,14 @@ void CpuManager::MultiCoreRunSuspendThread() {
138 auto core = kernel.CurrentPhysicalCoreIndex(); 139 auto core = kernel.CurrentPhysicalCoreIndex();
139 auto& scheduler = *kernel.CurrentScheduler(); 140 auto& scheduler = *kernel.CurrentScheduler();
140 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 141 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
142 current_thread->DisableDispatch();
143
141 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); 144 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
142 ASSERT(scheduler.ContextSwitchPending());
143 ASSERT(core == kernel.CurrentPhysicalCoreIndex()); 145 ASSERT(core == kernel.CurrentPhysicalCoreIndex());
144 scheduler.RescheduleCurrentCore(); 146 scheduler.RescheduleCurrentCore();
145 } 147 }
146} 148}
147 149
148void CpuManager::MultiCorePause(bool paused) {
149 if (!paused) {
150 bool all_not_barrier = false;
151 while (!all_not_barrier) {
152 all_not_barrier = true;
153 for (const auto& data : core_data) {
154 all_not_barrier &= !data.is_running.load() && data.initialized.load();
155 }
156 }
157 for (auto& data : core_data) {
158 data.enter_barrier->Set();
159 }
160 if (paused_state.load()) {
161 bool all_barrier = false;
162 while (!all_barrier) {
163 all_barrier = true;
164 for (const auto& data : core_data) {
165 all_barrier &= data.is_paused.load() && data.initialized.load();
166 }
167 }
168 for (auto& data : core_data) {
169 data.exit_barrier->Set();
170 }
171 }
172 } else {
173 /// Wait until all cores are paused.
174 bool all_barrier = false;
175 while (!all_barrier) {
176 all_barrier = true;
177 for (const auto& data : core_data) {
178 all_barrier &= data.is_paused.load() && data.initialized.load();
179 }
180 }
181 /// Don't release the barrier
182 }
183 paused_state = paused;
184}
185
186/////////////////////////////////////////////////////////////////////////////// 150///////////////////////////////////////////////////////////////////////////////
187/// SingleCore /// 151/// SingleCore ///
188/////////////////////////////////////////////////////////////////////////////// 152///////////////////////////////////////////////////////////////////////////////
@@ -200,12 +164,10 @@ void CpuManager::SingleCoreRunGuestLoop() {
200 auto& kernel = system.Kernel(); 164 auto& kernel = system.Kernel();
201 while (true) { 165 while (true) {
202 auto* physical_core = &kernel.CurrentPhysicalCore(); 166 auto* physical_core = &kernel.CurrentPhysicalCore();
203 system.EnterDynarmicProfile();
204 if (!physical_core->IsInterrupted()) { 167 if (!physical_core->IsInterrupted()) {
205 physical_core->Run(); 168 physical_core->Run();
206 physical_core = &kernel.CurrentPhysicalCore(); 169 physical_core = &kernel.CurrentPhysicalCore();
207 } 170 }
208 system.ExitDynarmicProfile();
209 kernel.SetIsPhantomModeForSingleCore(true); 171 kernel.SetIsPhantomModeForSingleCore(true);
210 system.CoreTiming().Advance(); 172 system.CoreTiming().Advance();
211 kernel.SetIsPhantomModeForSingleCore(false); 173 kernel.SetIsPhantomModeForSingleCore(false);
@@ -235,8 +197,9 @@ void CpuManager::SingleCoreRunSuspendThread() {
235 auto core = kernel.GetCurrentHostThreadID(); 197 auto core = kernel.GetCurrentHostThreadID();
236 auto& scheduler = *kernel.CurrentScheduler(); 198 auto& scheduler = *kernel.CurrentScheduler();
237 Kernel::KThread* current_thread = scheduler.GetCurrentThread(); 199 Kernel::KThread* current_thread = scheduler.GetCurrentThread();
200 current_thread->DisableDispatch();
201
238 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); 202 Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
239 ASSERT(scheduler.ContextSwitchPending());
240 ASSERT(core == kernel.GetCurrentHostThreadID()); 203 ASSERT(core == kernel.GetCurrentHostThreadID());
241 scheduler.RescheduleCurrentCore(); 204 scheduler.RescheduleCurrentCore();
242 } 205 }
@@ -274,37 +237,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
274 } 237 }
275} 238}
276 239
277void CpuManager::SingleCorePause(bool paused) {
278 if (!paused) {
279 bool all_not_barrier = false;
280 while (!all_not_barrier) {
281 all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
282 }
283 core_data[0].enter_barrier->Set();
284 if (paused_state.load()) {
285 bool all_barrier = false;
286 while (!all_barrier) {
287 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
288 }
289 core_data[0].exit_barrier->Set();
290 }
291 } else {
292 /// Wait until all cores are paused.
293 bool all_barrier = false;
294 while (!all_barrier) {
295 all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
296 }
297 /// Don't release the barrier
298 }
299 paused_state = paused;
300}
301
302void CpuManager::Pause(bool paused) { 240void CpuManager::Pause(bool paused) {
303 if (is_multicore) { 241 std::scoped_lock lk{pause_lock};
304 MultiCorePause(paused); 242
305 } else { 243 if (pause_state == paused) {
306 SingleCorePause(paused); 244 return;
307 } 245 }
246
247 // Set the new state
248 pause_state.store(paused);
249
250 // Wake up any waiting threads
251 pause_state.notify_all();
252
253 // Wait for all threads to successfully change state before returning
254 pause_barrier->Sync();
308} 255}
309 256
310void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { 257void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
@@ -320,27 +267,29 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
320 Common::SetCurrentThreadName(name.c_str()); 267 Common::SetCurrentThreadName(name.c_str());
321 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 268 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
322 auto& data = core_data[core]; 269 auto& data = core_data[core];
323 data.enter_barrier = std::make_unique<Common::Event>();
324 data.exit_barrier = std::make_unique<Common::Event>();
325 data.host_context = Common::Fiber::ThreadToFiber(); 270 data.host_context = Common::Fiber::ThreadToFiber();
326 data.is_running = false;
327 data.initialized = true;
328 const bool sc_sync = !is_async_gpu && !is_multicore; 271 const bool sc_sync = !is_async_gpu && !is_multicore;
329 bool sc_sync_first_use = sc_sync; 272 bool sc_sync_first_use = sc_sync;
330 273
331 // Cleanup 274 // Cleanup
332 SCOPE_EXIT({ 275 SCOPE_EXIT({
333 data.host_context->Exit(); 276 data.host_context->Exit();
334 data.enter_barrier.reset();
335 data.exit_barrier.reset();
336 data.initialized = false;
337 MicroProfileOnThreadExit(); 277 MicroProfileOnThreadExit();
338 }); 278 });
339 279
340 /// Running 280 /// Running
341 while (running_mode) { 281 while (running_mode) {
342 data.is_running = false; 282 if (pause_state.load(std::memory_order_relaxed)) {
343 data.enter_barrier->Wait(); 283 // Wait for caller to acknowledge pausing
284 pause_barrier->Sync();
285
286 // Wait until unpaused
287 pause_state.wait(true, std::memory_order_relaxed);
288
289 // Wait for caller to acknowledge unpausing
290 pause_barrier->Sync();
291 }
292
344 if (sc_sync_first_use) { 293 if (sc_sync_first_use) {
345 system.GPU().ObtainContext(); 294 system.GPU().ObtainContext();
346 sc_sync_first_use = false; 295 sc_sync_first_use = false;
@@ -352,12 +301,7 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
352 } 301 }
353 302
354 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 303 auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
355 data.is_running = true;
356 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); 304 Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
357 data.is_running = false;
358 data.is_paused = true;
359 data.exit_barrier->Wait();
360 data.is_paused = false;
361 } 305 }
362} 306}
363 307
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index aee352245..ddd9691ca 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -69,13 +69,11 @@ private:
69 void MultiCoreRunGuestLoop(); 69 void MultiCoreRunGuestLoop();
70 void MultiCoreRunIdleThread(); 70 void MultiCoreRunIdleThread();
71 void MultiCoreRunSuspendThread(); 71 void MultiCoreRunSuspendThread();
72 void MultiCorePause(bool paused);
73 72
74 void SingleCoreRunGuestThread(); 73 void SingleCoreRunGuestThread();
75 void SingleCoreRunGuestLoop(); 74 void SingleCoreRunGuestLoop();
76 void SingleCoreRunIdleThread(); 75 void SingleCoreRunIdleThread();
77 void SingleCoreRunSuspendThread(); 76 void SingleCoreRunSuspendThread();
78 void SingleCorePause(bool paused);
79 77
80 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); 78 static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
81 79
@@ -83,16 +81,13 @@ private:
83 81
84 struct CoreData { 82 struct CoreData {
85 std::shared_ptr<Common::Fiber> host_context; 83 std::shared_ptr<Common::Fiber> host_context;
86 std::unique_ptr<Common::Event> enter_barrier;
87 std::unique_ptr<Common::Event> exit_barrier;
88 std::atomic<bool> is_running;
89 std::atomic<bool> is_paused;
90 std::atomic<bool> initialized;
91 std::jthread host_thread; 84 std::jthread host_thread;
92 }; 85 };
93 86
94 std::atomic<bool> running_mode{}; 87 std::atomic<bool> running_mode{};
95 std::atomic<bool> paused_state{}; 88 std::atomic<bool> pause_state{};
89 std::unique_ptr<Common::Barrier> pause_barrier{};
90 std::mutex pause_lock{};
96 91
97 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{}; 92 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
98 93
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index e3c4f80eb..443323390 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -140,7 +140,6 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
140 return 0x3C; 140 return 0x3C;
141 } 141 }
142 UNREACHABLE(); 142 UNREACHABLE();
143 return 0;
144} 143}
145 144
146u64 GetSignatureTypePaddingSize(SignatureType type) { 145u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -155,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
155 return 0x40; 154 return 0x40;
156 } 155 }
157 UNREACHABLE(); 156 UNREACHABLE();
158 return 0;
159} 157}
160 158
161SignatureType Ticket::GetSignatureType() const { 159SignatureType Ticket::GetSignatureType() const {
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
new file mode 100644
index 000000000..edf991d71
--- /dev/null
+++ b/src/core/debugger/debugger.cpp
@@ -0,0 +1,310 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <mutex>
6#include <thread>
7
8#include <boost/asio.hpp>
9#include <boost/process/async_pipe.hpp>
10
11#include "common/logging/log.h"
12#include "common/thread.h"
13#include "core/core.h"
14#include "core/debugger/debugger.h"
15#include "core/debugger/debugger_interface.h"
16#include "core/debugger/gdbstub.h"
17#include "core/hle/kernel/global_scheduler_context.h"
18
19template <typename Readable, typename Buffer, typename Callback>
20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
21 static_assert(std::is_trivial_v<Buffer>);
22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
23 r.async_read_some(
24 boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
25 if (!error.failed()) {
26 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
27 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
28 c(received_data);
29 }
30
31 AsyncReceiveInto(r, buffer, c);
32 });
33}
34
35template <typename Readable, typename Buffer>
36static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
37 static_assert(std::is_trivial_v<Buffer>);
38 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
39 size_t bytes_read = r.read_some(boost_buffer);
40 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
41 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
42 return received_data;
43}
44
45enum class SignalType {
46 Stopped,
47 ShuttingDown,
48};
49
50struct SignalInfo {
51 SignalType type;
52 Kernel::KThread* thread;
53};
54
55namespace Core {
56
57class DebuggerImpl : public DebuggerBackend {
58public:
59 explicit DebuggerImpl(Core::System& system_, u16 port)
60 : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
61 frontend = std::make_unique<GDBStub>(*this, system);
62 InitializeServer(port);
63 }
64
65 ~DebuggerImpl() override {
66 ShutdownServer();
67 }
68
69 bool SignalDebugger(SignalInfo signal_info) {
70 std::scoped_lock lk{connection_lock};
71
72 if (stopped) {
73 // Do not notify the debugger about another event.
74 // It should be ignored.
75 return false;
76 }
77
78 // Set up the state.
79 stopped = true;
80 info = signal_info;
81
82 // Write a single byte into the pipe to wake up the debug interface.
83 boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
84 return true;
85 }
86
87 std::span<const u8> ReadFromClient() override {
88 return ReceiveInto(client_socket, client_data);
89 }
90
91 void WriteToClient(std::span<const u8> data) override {
92 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
93 }
94
95 void SetActiveThread(Kernel::KThread* thread) override {
96 active_thread = thread;
97 }
98
99 Kernel::KThread* GetActiveThread() override {
100 return active_thread;
101 }
102
103private:
104 void InitializeServer(u16 port) {
105 using boost::asio::ip::tcp;
106
107 LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
108
109 // Run the connection thread.
110 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
111 try {
112 // Initialize the listening socket and accept a new client.
113 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
114 tcp::acceptor acceptor{io_context, endpoint};
115
116 acceptor.async_accept(client_socket, [](const auto&) {});
117 io_context.run_one();
118 io_context.restart();
119
120 if (stop_token.stop_requested()) {
121 return;
122 }
123
124 ThreadLoop(stop_token);
125 } catch (const std::exception& ex) {
126 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
127 }
128 });
129 }
130
131 void ShutdownServer() {
132 connection_thread.request_stop();
133 io_context.stop();
134 connection_thread.join();
135 }
136
137 void ThreadLoop(std::stop_token stop_token) {
138 Common::SetCurrentThreadName("yuzu:Debugger");
139
140 // Set up the client signals for new data.
141 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
142 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
143
144 // Stop the emulated CPU.
145 AllCoreStop();
146
147 // Set the active thread.
148 UpdateActiveThread();
149
150 // Set up the frontend.
151 frontend->Connected();
152
153 // Main event loop.
154 while (!stop_token.stop_requested() && io_context.run()) {
155 }
156 }
157
158 void PipeData(std::span<const u8> data) {
159 switch (info.type) {
160 case SignalType::Stopped:
161 // Stop emulation.
162 AllCoreStop();
163
164 // Notify the client.
165 active_thread = info.thread;
166 UpdateActiveThread();
167 frontend->Stopped(active_thread);
168
169 break;
170 case SignalType::ShuttingDown:
171 frontend->ShuttingDown();
172
173 // Wait for emulation to shut down gracefully now.
174 suspend.reset();
175 signal_pipe.close();
176 client_socket.shutdown(boost::asio::socket_base::shutdown_both);
177 LOG_INFO(Debug_GDBStub, "Shut down server");
178
179 break;
180 }
181 }
182
183 void ClientData(std::span<const u8> data) {
184 const auto actions{frontend->ClientData(data)};
185 for (const auto action : actions) {
186 switch (action) {
187 case DebuggerAction::Interrupt: {
188 {
189 std::scoped_lock lk{connection_lock};
190 stopped = true;
191 }
192 AllCoreStop();
193 UpdateActiveThread();
194 frontend->Stopped(active_thread);
195 break;
196 }
197 case DebuggerAction::Continue:
198 active_thread->SetStepState(Kernel::StepState::NotStepping);
199 ResumeInactiveThreads();
200 AllCoreResume();
201 break;
202 case DebuggerAction::StepThreadUnlocked:
203 active_thread->SetStepState(Kernel::StepState::StepPending);
204 ResumeInactiveThreads();
205 AllCoreResume();
206 break;
207 case DebuggerAction::StepThreadLocked:
208 active_thread->SetStepState(Kernel::StepState::StepPending);
209 SuspendInactiveThreads();
210 AllCoreResume();
211 break;
212 case DebuggerAction::ShutdownEmulation: {
213 // Suspend all threads and release any locks held
214 active_thread->RequestSuspend(Kernel::SuspendType::Debug);
215 SuspendInactiveThreads();
216 AllCoreResume();
217
218 // Spawn another thread that will exit after shutdown,
219 // to avoid a deadlock
220 Core::System* system_ref{&system};
221 std::thread t([system_ref] { system_ref->Exit(); });
222 t.detach();
223 break;
224 }
225 }
226 }
227 }
228
229 void AllCoreStop() {
230 if (!suspend) {
231 suspend = system.StallCPU();
232 }
233 }
234
235 void AllCoreResume() {
236 stopped = false;
237 system.UnstallCPU();
238 suspend.reset();
239 }
240
241 void SuspendInactiveThreads() {
242 for (auto* thread : ThreadList()) {
243 if (thread != active_thread) {
244 thread->RequestSuspend(Kernel::SuspendType::Debug);
245 }
246 }
247 }
248
249 void ResumeInactiveThreads() {
250 for (auto* thread : ThreadList()) {
251 if (thread != active_thread) {
252 thread->Resume(Kernel::SuspendType::Debug);
253 thread->SetStepState(Kernel::StepState::NotStepping);
254 }
255 }
256 }
257
258 void UpdateActiveThread() {
259 const auto& threads{ThreadList()};
260 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
261 active_thread = threads[0];
262 }
263 active_thread->Resume(Kernel::SuspendType::Debug);
264 active_thread->SetStepState(Kernel::StepState::NotStepping);
265 }
266
267 const std::vector<Kernel::KThread*>& ThreadList() {
268 return system.GlobalSchedulerContext().GetThreadList();
269 }
270
271private:
272 System& system;
273 std::unique_ptr<DebuggerFrontend> frontend;
274
275 std::jthread connection_thread;
276 std::mutex connection_lock;
277 boost::asio::io_context io_context;
278 boost::process::async_pipe signal_pipe;
279 boost::asio::ip::tcp::socket client_socket;
280 std::optional<std::unique_lock<std::mutex>> suspend;
281
282 SignalInfo info;
283 Kernel::KThread* active_thread;
284 bool pipe_data;
285 bool stopped;
286
287 std::array<u8, 4096> client_data;
288};
289
290Debugger::Debugger(Core::System& system, u16 port) {
291 try {
292 impl = std::make_unique<DebuggerImpl>(system, port);
293 } catch (const std::exception& ex) {
294 LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
295 }
296}
297
298Debugger::~Debugger() = default;
299
300bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
301 return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
302}
303
304void Debugger::NotifyShutdown() {
305 if (impl) {
306 impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
307 }
308}
309
310} // namespace Core
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
new file mode 100644
index 000000000..f9738ca3d
--- /dev/null
+++ b/src/core/debugger/debugger.h
@@ -0,0 +1,46 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Kernel {
11class KThread;
12}
13
14namespace Core {
15class System;
16
17class DebuggerImpl;
18
19class Debugger {
20public:
21 /**
22 * Blocks and waits for a connection on localhost, port `server_port`.
23 * Does not create the debugger if the port is already in use.
24 */
25 explicit Debugger(Core::System& system, u16 server_port);
26 ~Debugger();
27
28 /**
29 * Notify the debugger that the given thread is stopped
30 * (due to a breakpoint, or due to stopping after a successful step).
31 *
32 * The debugger will asynchronously halt emulation after the notification has
33 * occurred. If another thread attempts to notify before emulation has stopped,
34 * it is ignored and this method will return false. Otherwise it will return true.
35 */
36 bool NotifyThreadStopped(Kernel::KThread* thread);
37
38 /**
39 * Notify the debugger that a shutdown is being performed now and disconnect.
40 */
41 void NotifyShutdown();
42
43private:
44 std::unique_ptr<DebuggerImpl> impl;
45};
46} // namespace Core
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
new file mode 100644
index 000000000..c0bb4ecaf
--- /dev/null
+++ b/src/core/debugger/debugger_interface.h
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <span>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Kernel {
13class KThread;
14}
15
16namespace Core {
17
18enum class DebuggerAction {
19 Interrupt, ///< Stop emulation as soon as possible.
20 Continue, ///< Resume emulation.
21 StepThreadLocked, ///< Step the currently-active thread without resuming others.
22 StepThreadUnlocked, ///< Step the currently-active thread and resume others.
23 ShutdownEmulation, ///< Shut down the emulator.
24};
25
26class DebuggerBackend {
27public:
28 virtual ~DebuggerBackend() = default;
29
30 /**
31 * Can be invoked from a callback to synchronously wait for more data.
32 * Will return as soon as least one byte is received. Reads up to 4096 bytes.
33 */
34 virtual std::span<const u8> ReadFromClient() = 0;
35
36 /**
37 * Can be invoked from a callback to write data to the client.
38 * Returns immediately after the data is sent.
39 */
40 virtual void WriteToClient(std::span<const u8> data) = 0;
41
42 /**
43 * Gets the currently active thread when the debugger is stopped.
44 */
45 virtual Kernel::KThread* GetActiveThread() = 0;
46
47 /**
48 * Sets the currently active thread when the debugger is stopped.
49 */
50 virtual void SetActiveThread(Kernel::KThread* thread) = 0;
51};
52
53class DebuggerFrontend {
54public:
55 explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
56
57 virtual ~DebuggerFrontend() = default;
58
59 /**
60 * Called after the client has successfully connected to the port.
61 */
62 virtual void Connected() = 0;
63
64 /**
65 * Called when emulation has stopped.
66 */
67 virtual void Stopped(Kernel::KThread* thread) = 0;
68
69 /**
70 * Called when emulation is shutting down.
71 */
72 virtual void ShuttingDown() = 0;
73
74 /**
75 * Called when new data is asynchronously received on the client socket.
76 * A list of actions to perform is returned.
77 */
78 [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
79
80protected:
81 DebuggerBackend& backend;
82};
83
84} // namespace Core
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
new file mode 100644
index 000000000..52e76f659
--- /dev/null
+++ b/src/core/debugger/gdbstub.cpp
@@ -0,0 +1,620 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <atomic>
5#include <numeric>
6#include <optional>
7#include <thread>
8
9#include <boost/algorithm/string.hpp>
10
11#include "common/hex_util.h"
12#include "common/logging/log.h"
13#include "common/scope_exit.h"
14#include "core/arm/arm_interface.h"
15#include "core/core.h"
16#include "core/debugger/gdbstub.h"
17#include "core/debugger/gdbstub_arch.h"
18#include "core/hle/kernel/k_page_table.h"
19#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/k_thread.h"
21#include "core/loader/loader.h"
22#include "core/memory.h"
23
24namespace Core {
25
26constexpr char GDB_STUB_START = '$';
27constexpr char GDB_STUB_END = '#';
28constexpr char GDB_STUB_ACK = '+';
29constexpr char GDB_STUB_NACK = '-';
30constexpr char GDB_STUB_INT3 = 0x03;
31constexpr int GDB_STUB_SIGTRAP = 5;
32
33constexpr char GDB_STUB_REPLY_ERR[] = "E01";
34constexpr char GDB_STUB_REPLY_OK[] = "OK";
35constexpr char GDB_STUB_REPLY_EMPTY[] = "";
36
37static u8 CalculateChecksum(std::string_view data) {
38 return std::accumulate(data.begin(), data.end(), u8{0},
39 [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
40}
41
42static std::string EscapeGDB(std::string_view data) {
43 std::string escaped;
44 escaped.reserve(data.size());
45
46 for (char c : data) {
47 switch (c) {
48 case '#':
49 escaped += "}\x03";
50 break;
51 case '$':
52 escaped += "}\x04";
53 break;
54 case '*':
55 escaped += "}\x0a";
56 break;
57 case '}':
58 escaped += "}\x5d";
59 break;
60 default:
61 escaped += c;
62 break;
63 }
64 }
65
66 return escaped;
67}
68
69static std::string EscapeXML(std::string_view data) {
70 std::string escaped;
71 escaped.reserve(data.size());
72
73 for (char c : data) {
74 switch (c) {
75 case '&':
76 escaped += "&amp;";
77 break;
78 case '"':
79 escaped += "&quot;";
80 break;
81 case '<':
82 escaped += "&lt;";
83 break;
84 case '>':
85 escaped += "&gt;";
86 break;
87 default:
88 escaped += c;
89 break;
90 }
91 }
92
93 return escaped;
94}
95
96GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
97 : DebuggerFrontend(backend_), system{system_} {
98 if (system.CurrentProcess()->Is64BitProcess()) {
99 arch = std::make_unique<GDBStubA64>();
100 } else {
101 arch = std::make_unique<GDBStubA32>();
102 }
103}
104
105GDBStub::~GDBStub() = default;
106
107void GDBStub::Connected() {}
108
109void GDBStub::ShuttingDown() {}
110
111void GDBStub::Stopped(Kernel::KThread* thread) {
112 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
113}
114
115std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
116 std::vector<DebuggerAction> actions;
117 current_command.insert(current_command.end(), data.begin(), data.end());
118
119 while (current_command.size() != 0) {
120 ProcessData(actions);
121 }
122
123 return actions;
124}
125
126void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
127 const char c{current_command[0]};
128
129 // Acknowledgement
130 if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
131 current_command.erase(current_command.begin());
132 return;
133 }
134
135 // Interrupt
136 if (c == GDB_STUB_INT3) {
137 LOG_INFO(Debug_GDBStub, "Received interrupt");
138 current_command.erase(current_command.begin());
139 actions.push_back(DebuggerAction::Interrupt);
140 SendStatus(GDB_STUB_ACK);
141 return;
142 }
143
144 // Otherwise, require the data to be the start of a command
145 if (c != GDB_STUB_START) {
146 LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
147 current_command.clear();
148 SendStatus(GDB_STUB_NACK);
149 return;
150 }
151
152 // Continue reading until command is complete
153 while (CommandEnd() == current_command.end()) {
154 const auto new_data{backend.ReadFromClient()};
155 current_command.insert(current_command.end(), new_data.begin(), new_data.end());
156 }
157
158 // Execute and respond to GDB
159 const auto command{DetachCommand()};
160
161 if (command) {
162 SendStatus(GDB_STUB_ACK);
163 ExecuteCommand(*command, actions);
164 } else {
165 SendStatus(GDB_STUB_NACK);
166 }
167}
168
169void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
170 LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
171
172 if (packet.length() == 0) {
173 SendReply(GDB_STUB_REPLY_ERR);
174 return;
175 }
176
177 if (packet.starts_with("vCont")) {
178 HandleVCont(packet.substr(5), actions);
179 return;
180 }
181
182 std::string_view command{packet.substr(1, packet.size())};
183
184 switch (packet[0]) {
185 case 'H': {
186 Kernel::KThread* thread{nullptr};
187 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
188 if (thread_id >= 1) {
189 thread = GetThreadByID(thread_id);
190 } else {
191 thread = backend.GetActiveThread();
192 }
193
194 if (thread) {
195 SendReply(GDB_STUB_REPLY_OK);
196 backend.SetActiveThread(thread);
197 } else {
198 SendReply(GDB_STUB_REPLY_ERR);
199 }
200 break;
201 }
202 case 'T': {
203 s64 thread_id{strtoll(command.data(), nullptr, 16)};
204 if (GetThreadByID(thread_id)) {
205 SendReply(GDB_STUB_REPLY_OK);
206 } else {
207 SendReply(GDB_STUB_REPLY_ERR);
208 }
209 break;
210 }
211 case 'Q':
212 case 'q':
213 HandleQuery(command);
214 break;
215 case '?':
216 SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
217 break;
218 case 'k':
219 LOG_INFO(Debug_GDBStub, "Shutting down emulation");
220 actions.push_back(DebuggerAction::ShutdownEmulation);
221 break;
222 case 'g':
223 SendReply(arch->ReadRegisters(backend.GetActiveThread()));
224 break;
225 case 'G':
226 arch->WriteRegisters(backend.GetActiveThread(), command);
227 SendReply(GDB_STUB_REPLY_OK);
228 break;
229 case 'p': {
230 const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
231 SendReply(arch->RegRead(backend.GetActiveThread(), reg));
232 break;
233 }
234 case 'P': {
235 const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
236 const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
237 arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
238 break;
239 }
240 case 'm': {
241 const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
242 const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
243 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
244
245 if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
246 std::vector<u8> mem(size);
247 system.Memory().ReadBlock(addr, mem.data(), size);
248
249 SendReply(Common::HexToString(mem));
250 } else {
251 SendReply(GDB_STUB_REPLY_ERR);
252 }
253 break;
254 }
255 case 'M': {
256 const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
257 const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
258
259 const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
260 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
261
262 const auto mem_substr{std::string_view(command).substr(mem_sep)};
263 const auto mem{Common::HexStringToVector(mem_substr, false)};
264
265 if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
266 system.Memory().WriteBlock(addr, mem.data(), size);
267 system.InvalidateCpuInstructionCacheRange(addr, size);
268 SendReply(GDB_STUB_REPLY_OK);
269 } else {
270 SendReply(GDB_STUB_REPLY_ERR);
271 }
272 break;
273 }
274 case 's':
275 actions.push_back(DebuggerAction::StepThreadLocked);
276 break;
277 case 'C':
278 case 'c':
279 actions.push_back(DebuggerAction::Continue);
280 break;
281 case 'Z': {
282 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
283 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
284
285 if (system.Memory().IsValidVirtualAddress(addr)) {
286 replaced_instructions[addr] = system.Memory().Read32(addr);
287 system.Memory().Write32(addr, arch->BreakpointInstruction());
288 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
289
290 SendReply(GDB_STUB_REPLY_OK);
291 } else {
292 SendReply(GDB_STUB_REPLY_ERR);
293 }
294 break;
295 }
296 case 'z': {
297 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
298 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
299
300 const auto orig_insn{replaced_instructions.find(addr)};
301 if (system.Memory().IsValidVirtualAddress(addr) &&
302 orig_insn != replaced_instructions.end()) {
303 system.Memory().Write32(addr, orig_insn->second);
304 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
305 replaced_instructions.erase(addr);
306
307 SendReply(GDB_STUB_REPLY_OK);
308 } else {
309 SendReply(GDB_STUB_REPLY_ERR);
310 }
311 break;
312 }
313 default:
314 SendReply(GDB_STUB_REPLY_EMPTY);
315 break;
316 }
317}
318
319// Structure offsets are from Atmosphere
320// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
321
322static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
323 const Kernel::KThread* thread) {
324 // Read thread type from TLS
325 const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
326 const VAddr argument_thread_type{thread->GetArgument()};
327
328 if (argument_thread_type && tls_thread_type != argument_thread_type) {
329 // Probably not created by nnsdk, no name available.
330 return std::nullopt;
331 }
332
333 if (!tls_thread_type) {
334 return std::nullopt;
335 }
336
337 const u16 version{memory.Read16(tls_thread_type + 0x26)};
338 VAddr name_pointer{};
339 if (version == 1) {
340 name_pointer = memory.Read32(tls_thread_type + 0xe4);
341 } else {
342 name_pointer = memory.Read32(tls_thread_type + 0xe8);
343 }
344
345 if (!name_pointer) {
346 // No name provided.
347 return std::nullopt;
348 }
349
350 return memory.ReadCString(name_pointer, 256);
351}
352
353static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
354 const Kernel::KThread* thread) {
355 // Read thread type from TLS
356 const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
357 const VAddr argument_thread_type{thread->GetArgument()};
358
359 if (argument_thread_type && tls_thread_type != argument_thread_type) {
360 // Probably not created by nnsdk, no name available.
361 return std::nullopt;
362 }
363
364 if (!tls_thread_type) {
365 return std::nullopt;
366 }
367
368 const u16 version{memory.Read16(tls_thread_type + 0x46)};
369 VAddr name_pointer{};
370 if (version == 1) {
371 name_pointer = memory.Read64(tls_thread_type + 0x1a0);
372 } else {
373 name_pointer = memory.Read64(tls_thread_type + 0x1a8);
374 }
375
376 if (!name_pointer) {
377 // No name provided.
378 return std::nullopt;
379 }
380
381 return memory.ReadCString(name_pointer, 256);
382}
383
384static std::optional<std::string> GetThreadName(Core::System& system,
385 const Kernel::KThread* thread) {
386 if (system.CurrentProcess()->Is64BitProcess()) {
387 return GetNameFromThreadType64(system.Memory(), thread);
388 } else {
389 return GetNameFromThreadType32(system.Memory(), thread);
390 }
391}
392
393static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
394 switch (thread->GetWaitReasonForDebugging()) {
395 case Kernel::ThreadWaitReasonForDebugging::Sleep:
396 return "Sleep";
397 case Kernel::ThreadWaitReasonForDebugging::IPC:
398 return "IPC";
399 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
400 return "Synchronization";
401 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
402 return "ConditionVar";
403 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
404 return "Arbitration";
405 case Kernel::ThreadWaitReasonForDebugging::Suspended:
406 return "Suspended";
407 default:
408 return "Unknown";
409 }
410}
411
412static std::string GetThreadState(const Kernel::KThread* thread) {
413 switch (thread->GetState()) {
414 case Kernel::ThreadState::Initialized:
415 return "Initialized";
416 case Kernel::ThreadState::Waiting:
417 return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
418 case Kernel::ThreadState::Runnable:
419 return "Runnable";
420 case Kernel::ThreadState::Terminated:
421 return "Terminated";
422 default:
423 return "Unknown";
424 }
425}
426
427static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
428 const auto amount{request.substr(request.find(',') + 1)};
429 const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
430 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
431
432 if (offset_val + amount_val > buffer.size()) {
433 return fmt::format("l{}", buffer.substr(offset_val));
434 } else {
435 return fmt::format("m{}", buffer.substr(offset_val, amount_val));
436 }
437}
438
439void GDBStub::HandleQuery(std::string_view command) {
440 if (command.starts_with("TStatus")) {
441 // no tracepoint support
442 SendReply("T0");
443 } else if (command.starts_with("Supported")) {
444 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
445 "vContSupported+;QStartNoAckMode+");
446 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
447 const auto target_xml{arch->GetTargetXML()};
448 SendReply(PaginateBuffer(target_xml, command.substr(30)));
449 } else if (command.starts_with("Offsets")) {
450 Loader::AppLoader::Modules modules;
451 system.GetAppLoader().ReadNSOModules(modules);
452
453 const auto main = std::find_if(modules.begin(), modules.end(),
454 [](const auto& key) { return key.second == "main"; });
455 if (main != modules.end()) {
456 SendReply(fmt::format("TextSeg={:x}", main->first));
457 } else {
458 SendReply(fmt::format("TextSeg={:x}",
459 system.CurrentProcess()->PageTable().GetCodeRegionStart()));
460 }
461 } else if (command.starts_with("Xfer:libraries:read::")) {
462 Loader::AppLoader::Modules modules;
463 system.GetAppLoader().ReadNSOModules(modules);
464
465 std::string buffer;
466 buffer += R"(<?xml version="1.0"?>)";
467 buffer += "<library-list>";
468 for (const auto& [base, name] : modules) {
469 buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
470 EscapeXML(name), base);
471 }
472 buffer += "</library-list>";
473
474 SendReply(PaginateBuffer(buffer, command.substr(21)));
475 } else if (command.starts_with("fThreadInfo")) {
476 // beginning of list
477 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
478 std::vector<std::string> thread_ids;
479 for (const auto& thread : threads) {
480 thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
481 }
482 SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
483 } else if (command.starts_with("sThreadInfo")) {
484 // end of list
485 SendReply("l");
486 } else if (command.starts_with("Xfer:threads:read::")) {
487 std::string buffer;
488 buffer += R"(<?xml version="1.0"?>)";
489 buffer += "<threads>";
490
491 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
492 for (const auto* thread : threads) {
493 auto thread_name{GetThreadName(system, thread)};
494 if (!thread_name) {
495 thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
496 }
497
498 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
499 thread->GetThreadID(), thread->GetActiveCore(),
500 EscapeXML(*thread_name), GetThreadState(thread));
501 }
502
503 buffer += "</threads>";
504
505 SendReply(PaginateBuffer(buffer, command.substr(19)));
506 } else if (command.starts_with("Attached")) {
507 SendReply("0");
508 } else if (command.starts_with("StartNoAckMode")) {
509 no_ack = true;
510 SendReply(GDB_STUB_REPLY_OK);
511 } else {
512 SendReply(GDB_STUB_REPLY_EMPTY);
513 }
514}
515
516void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
517 if (command == "?") {
518 // Continuing and stepping are supported
519 // (signal is ignored, but required for GDB to use vCont)
520 SendReply("vCont;c;C;s;S");
521 return;
522 }
523
524 Kernel::KThread* stepped_thread{nullptr};
525 bool lock_execution{true};
526
527 std::vector<std::string> entries;
528 boost::split(entries, command.substr(1), boost::is_any_of(";"));
529 for (const auto& thread_action : entries) {
530 std::vector<std::string> parts;
531 boost::split(parts, thread_action, boost::is_any_of(":"));
532
533 if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
534 lock_execution = false;
535 }
536 if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
537 stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
538 }
539 }
540
541 if (stepped_thread) {
542 backend.SetActiveThread(stepped_thread);
543 actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
544 : DebuggerAction::StepThreadUnlocked);
545 } else {
546 actions.push_back(DebuggerAction::Continue);
547 }
548}
549
550Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
551 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
552 for (auto* thread : threads) {
553 if (thread->GetThreadID() == thread_id) {
554 return thread;
555 }
556 }
557
558 return nullptr;
559}
560
561std::vector<char>::const_iterator GDBStub::CommandEnd() const {
562 // Find the end marker
563 const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
564
565 // Require the checksum to be present
566 return std::min(end + 2, current_command.end());
567}
568
569std::optional<std::string> GDBStub::DetachCommand() {
570 // Slice the string part from the beginning to the end marker
571 const auto end{CommandEnd()};
572
573 // Extract possible command data
574 std::string data(current_command.data(), end - current_command.begin() + 1);
575
576 // Shift over the remaining contents
577 current_command.erase(current_command.begin(), end + 1);
578
579 // Validate received command
580 if (data[0] != GDB_STUB_START) {
581 LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
582 return std::nullopt;
583 }
584
585 u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
586 u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
587
588 // Verify checksum
589 if (calculated != received) {
590 LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
591 calculated, received);
592 return std::nullopt;
593 }
594
595 return data.substr(1, data.size() - 4);
596}
597
598void GDBStub::SendReply(std::string_view data) {
599 const auto escaped{EscapeGDB(data)};
600 const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
601 CalculateChecksum(escaped))};
602 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
603
604 // C++ string support is complete rubbish
605 const u8* output_begin = reinterpret_cast<const u8*>(output.data());
606 const u8* output_end = output_begin + output.size();
607 backend.WriteToClient(std::span<const u8>(output_begin, output_end));
608}
609
610void GDBStub::SendStatus(char status) {
611 if (no_ack) {
612 return;
613 }
614
615 std::array<u8, 1> buf = {static_cast<u8>(status)};
616 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
617 backend.WriteToClient(buf);
618}
619
620} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
new file mode 100644
index 000000000..ec934c77e
--- /dev/null
+++ b/src/core/debugger/gdbstub.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <map>
7#include <memory>
8#include <optional>
9#include <string_view>
10#include <vector>
11
12#include "core/debugger/debugger_interface.h"
13#include "core/debugger/gdbstub_arch.h"
14
15namespace Core {
16
17class System;
18
19class GDBStub : public DebuggerFrontend {
20public:
21 explicit GDBStub(DebuggerBackend& backend, Core::System& system);
22 ~GDBStub() override;
23
24 void Connected() override;
25 void Stopped(Kernel::KThread* thread) override;
26 void ShuttingDown() override;
27 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
28
29private:
30 void ProcessData(std::vector<DebuggerAction>& actions);
31 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
32 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
33 void HandleQuery(std::string_view command);
34 std::vector<char>::const_iterator CommandEnd() const;
35 std::optional<std::string> DetachCommand();
36 Kernel::KThread* GetThreadByID(u64 thread_id);
37
38 void SendReply(std::string_view data);
39 void SendStatus(char status);
40
41private:
42 Core::System& system;
43 std::unique_ptr<GDBStubArch> arch;
44 std::vector<char> current_command;
45 std::map<VAddr, u32> replaced_instructions;
46 bool no_ack{};
47};
48
49} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
new file mode 100644
index 000000000..750c353b9
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -0,0 +1,483 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/hex_util.h"
5#include "core/debugger/gdbstub_arch.h"
6#include "core/hle/kernel/k_thread.h"
7
8namespace Core {
9
10template <typename T>
11static T HexToValue(std::string_view hex) {
12 static_assert(std::is_trivially_copyable_v<T>);
13 T value{};
14 const auto mem{Common::HexStringToVector(hex, false)};
15 std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
16 return value;
17}
18
19template <typename T>
20static std::string ValueToHex(const T value) {
21 static_assert(std::is_trivially_copyable_v<T>);
22 std::array<u8, sizeof(T)> mem{};
23 std::memcpy(mem.data(), &value, sizeof(T));
24 return Common::HexToString(mem);
25}
26
27template <typename T>
28static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
29 static_assert(std::is_trivially_copyable_v<T>);
30 T value{};
31 std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
32 sizeof(T));
33 return value;
34}
35
36template <typename T>
37static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
38 static_assert(std::is_trivially_copyable_v<T>);
39 std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
40}
41
42// For sample XML files see the GDB source /gdb/features
43// This XML defines what the registers are for this specific ARM device
44std::string GDBStubA64::GetTargetXML() const {
45 constexpr const char* target_xml =
46 R"(<?xml version="1.0"?>
47<!DOCTYPE target SYSTEM "gdb-target.dtd">
48<target version="1.0">
49 <architecture>aarch64</architecture>
50 <feature name="org.gnu.gdb.aarch64.core">
51 <reg name="x0" bitsize="64"/>
52 <reg name="x1" bitsize="64"/>
53 <reg name="x2" bitsize="64"/>
54 <reg name="x3" bitsize="64"/>
55 <reg name="x4" bitsize="64"/>
56 <reg name="x5" bitsize="64"/>
57 <reg name="x6" bitsize="64"/>
58 <reg name="x7" bitsize="64"/>
59 <reg name="x8" bitsize="64"/>
60 <reg name="x9" bitsize="64"/>
61 <reg name="x10" bitsize="64"/>
62 <reg name="x11" bitsize="64"/>
63 <reg name="x12" bitsize="64"/>
64 <reg name="x13" bitsize="64"/>
65 <reg name="x14" bitsize="64"/>
66 <reg name="x15" bitsize="64"/>
67 <reg name="x16" bitsize="64"/>
68 <reg name="x17" bitsize="64"/>
69 <reg name="x18" bitsize="64"/>
70 <reg name="x19" bitsize="64"/>
71 <reg name="x20" bitsize="64"/>
72 <reg name="x21" bitsize="64"/>
73 <reg name="x22" bitsize="64"/>
74 <reg name="x23" bitsize="64"/>
75 <reg name="x24" bitsize="64"/>
76 <reg name="x25" bitsize="64"/>
77 <reg name="x26" bitsize="64"/>
78 <reg name="x27" bitsize="64"/>
79 <reg name="x28" bitsize="64"/>
80 <reg name="x29" bitsize="64"/>
81 <reg name="x30" bitsize="64"/>
82 <reg name="sp" bitsize="64" type="data_ptr"/>
83 <reg name="pc" bitsize="64" type="code_ptr"/>
84 <flags id="cpsr_flags" size="4">
85 <field name="SP" start="0" end="0"/>
86 <field name="" start="1" end="1"/>
87 <field name="EL" start="2" end="3"/>
88 <field name="nRW" start="4" end="4"/>
89 <field name="" start="5" end="5"/>
90 <field name="F" start="6" end="6"/>
91 <field name="I" start="7" end="7"/>
92 <field name="A" start="8" end="8"/>
93 <field name="D" start="9" end="9"/>
94 <field name="IL" start="20" end="20"/>
95 <field name="SS" start="21" end="21"/>
96 <field name="V" start="28" end="28"/>
97 <field name="C" start="29" end="29"/>
98 <field name="Z" start="30" end="30"/>
99 <field name="N" start="31" end="31"/>
100 </flags>
101 <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
102 </feature>
103 <feature name="org.gnu.gdb.aarch64.fpu">
104 <vector id="v2d" type="ieee_double" count="2"/>
105 <vector id="v2u" type="uint64" count="2"/>
106 <vector id="v2i" type="int64" count="2"/>
107 <vector id="v4f" type="ieee_single" count="4"/>
108 <vector id="v4u" type="uint32" count="4"/>
109 <vector id="v4i" type="int32" count="4"/>
110 <vector id="v8u" type="uint16" count="8"/>
111 <vector id="v8i" type="int16" count="8"/>
112 <vector id="v16u" type="uint8" count="16"/>
113 <vector id="v16i" type="int8" count="16"/>
114 <vector id="v1u" type="uint128" count="1"/>
115 <vector id="v1i" type="int128" count="1"/>
116 <union id="vnd">
117 <field name="f" type="v2d"/>
118 <field name="u" type="v2u"/>
119 <field name="s" type="v2i"/>
120 </union>
121 <union id="vns">
122 <field name="f" type="v4f"/>
123 <field name="u" type="v4u"/>
124 <field name="s" type="v4i"/>
125 </union>
126 <union id="vnh">
127 <field name="u" type="v8u"/>
128 <field name="s" type="v8i"/>
129 </union>
130 <union id="vnb">
131 <field name="u" type="v16u"/>
132 <field name="s" type="v16i"/>
133 </union>
134 <union id="vnq">
135 <field name="u" type="v1u"/>
136 <field name="s" type="v1i"/>
137 </union>
138 <union id="aarch64v">
139 <field name="d" type="vnd"/>
140 <field name="s" type="vns"/>
141 <field name="h" type="vnh"/>
142 <field name="b" type="vnb"/>
143 <field name="q" type="vnq"/>
144 </union>
145 <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
146 <reg name="v1" bitsize="128" type="aarch64v" />
147 <reg name="v2" bitsize="128" type="aarch64v" />
148 <reg name="v3" bitsize="128" type="aarch64v" />
149 <reg name="v4" bitsize="128" type="aarch64v" />
150 <reg name="v5" bitsize="128" type="aarch64v" />
151 <reg name="v6" bitsize="128" type="aarch64v" />
152 <reg name="v7" bitsize="128" type="aarch64v" />
153 <reg name="v8" bitsize="128" type="aarch64v" />
154 <reg name="v9" bitsize="128" type="aarch64v" />
155 <reg name="v10" bitsize="128" type="aarch64v"/>
156 <reg name="v11" bitsize="128" type="aarch64v"/>
157 <reg name="v12" bitsize="128" type="aarch64v"/>
158 <reg name="v13" bitsize="128" type="aarch64v"/>
159 <reg name="v14" bitsize="128" type="aarch64v"/>
160 <reg name="v15" bitsize="128" type="aarch64v"/>
161 <reg name="v16" bitsize="128" type="aarch64v"/>
162 <reg name="v17" bitsize="128" type="aarch64v"/>
163 <reg name="v18" bitsize="128" type="aarch64v"/>
164 <reg name="v19" bitsize="128" type="aarch64v"/>
165 <reg name="v20" bitsize="128" type="aarch64v"/>
166 <reg name="v21" bitsize="128" type="aarch64v"/>
167 <reg name="v22" bitsize="128" type="aarch64v"/>
168 <reg name="v23" bitsize="128" type="aarch64v"/>
169 <reg name="v24" bitsize="128" type="aarch64v"/>
170 <reg name="v25" bitsize="128" type="aarch64v"/>
171 <reg name="v26" bitsize="128" type="aarch64v"/>
172 <reg name="v27" bitsize="128" type="aarch64v"/>
173 <reg name="v28" bitsize="128" type="aarch64v"/>
174 <reg name="v29" bitsize="128" type="aarch64v"/>
175 <reg name="v30" bitsize="128" type="aarch64v"/>
176 <reg name="v31" bitsize="128" type="aarch64v"/>
177 <reg name="fpsr" bitsize="32"/>
178 <reg name="fpcr" bitsize="32"/>
179 </feature>
180</target>)";
181
182 return target_xml;
183}
184
185std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
186 if (!thread) {
187 return "";
188 }
189
190 const auto& context{thread->GetContext64()};
191 const auto& gprs{context.cpu_registers};
192 const auto& fprs{context.vector_registers};
193
194 if (id <= SP_REGISTER) {
195 return ValueToHex(gprs[id]);
196 } else if (id == PC_REGISTER) {
197 return ValueToHex(context.pc);
198 } else if (id == PSTATE_REGISTER) {
199 return ValueToHex(context.pstate);
200 } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
201 return ValueToHex(fprs[id - Q0_REGISTER]);
202 } else if (id == FPSR_REGISTER) {
203 return ValueToHex(context.fpsr);
204 } else if (id == FPCR_REGISTER) {
205 return ValueToHex(context.fpcr);
206 } else {
207 return "";
208 }
209}
210
211void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
212 if (!thread) {
213 return;
214 }
215
216 auto& context{thread->GetContext64()};
217
218 if (id <= SP_REGISTER) {
219 context.cpu_registers[id] = HexToValue<u64>(value);
220 } else if (id == PC_REGISTER) {
221 context.pc = HexToValue<u64>(value);
222 } else if (id == PSTATE_REGISTER) {
223 context.pstate = HexToValue<u32>(value);
224 } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
225 context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
226 } else if (id == FPSR_REGISTER) {
227 context.fpsr = HexToValue<u32>(value);
228 } else if (id == FPCR_REGISTER) {
229 context.fpcr = HexToValue<u32>(value);
230 }
231}
232
233std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
234 std::string output;
235
236 for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
237 output += RegRead(thread, reg);
238 }
239
240 return output;
241}
242
243void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
244 for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
245 if (reg <= SP_REGISTER || reg == PC_REGISTER) {
246 RegWrite(thread, reg, register_data.substr(i, 16));
247 i += 16;
248 } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
249 RegWrite(thread, reg, register_data.substr(i, 8));
250 i += 8;
251 } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
252 RegWrite(thread, reg, register_data.substr(i, 32));
253 i += 32;
254 }
255 }
256}
257
258std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
259 return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
260 RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
261 LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
262}
263
264u32 GDBStubA64::BreakpointInstruction() const {
265 // A64: brk #0
266 return 0xd4200000;
267}
268
269std::string GDBStubA32::GetTargetXML() const {
270 constexpr const char* target_xml =
271 R"(<?xml version="1.0"?>
272<!DOCTYPE target SYSTEM "gdb-target.dtd">
273<target version="1.0">
274 <architecture>arm</architecture>
275 <feature name="org.gnu.gdb.arm.core">
276 <reg name="r0" bitsize="32" type="uint32"/>
277 <reg name="r1" bitsize="32" type="uint32"/>
278 <reg name="r2" bitsize="32" type="uint32"/>
279 <reg name="r3" bitsize="32" type="uint32"/>
280 <reg name="r4" bitsize="32" type="uint32"/>
281 <reg name="r5" bitsize="32" type="uint32"/>
282 <reg name="r6" bitsize="32" type="uint32"/>
283 <reg name="r7" bitsize="32" type="uint32"/>
284 <reg name="r8" bitsize="32" type="uint32"/>
285 <reg name="r9" bitsize="32" type="uint32"/>
286 <reg name="r10" bitsize="32" type="uint32"/>
287 <reg name="r11" bitsize="32" type="uint32"/>
288 <reg name="r12" bitsize="32" type="uint32"/>
289 <reg name="sp" bitsize="32" type="data_ptr"/>
290 <reg name="lr" bitsize="32" type="code_ptr"/>
291 <reg name="pc" bitsize="32" type="code_ptr"/>
292 <!-- The CPSR is register 25, rather than register 16, because
293 the FPA registers historically were placed between the PC
294 and the CPSR in the "g" packet. -->
295 <reg name="cpsr" bitsize="32" regnum="25"/>
296 </feature>
297 <feature name="org.gnu.gdb.arm.vfp">
298 <vector id="neon_uint8x8" type="uint8" count="8"/>
299 <vector id="neon_uint16x4" type="uint16" count="4"/>
300 <vector id="neon_uint32x2" type="uint32" count="2"/>
301 <vector id="neon_float32x2" type="ieee_single" count="2"/>
302 <union id="neon_d">
303 <field name="u8" type="neon_uint8x8"/>
304 <field name="u16" type="neon_uint16x4"/>
305 <field name="u32" type="neon_uint32x2"/>
306 <field name="u64" type="uint64"/>
307 <field name="f32" type="neon_float32x2"/>
308 <field name="f64" type="ieee_double"/>
309 </union>
310 <vector id="neon_uint8x16" type="uint8" count="16"/>
311 <vector id="neon_uint16x8" type="uint16" count="8"/>
312 <vector id="neon_uint32x4" type="uint32" count="4"/>
313 <vector id="neon_uint64x2" type="uint64" count="2"/>
314 <vector id="neon_float32x4" type="ieee_single" count="4"/>
315 <vector id="neon_float64x2" type="ieee_double" count="2"/>
316 <union id="neon_q">
317 <field name="u8" type="neon_uint8x16"/>
318 <field name="u16" type="neon_uint16x8"/>
319 <field name="u32" type="neon_uint32x4"/>
320 <field name="u64" type="neon_uint64x2"/>
321 <field name="f32" type="neon_float32x4"/>
322 <field name="f64" type="neon_float64x2"/>
323 </union>
324 <reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
325 <reg name="d1" bitsize="64" type="neon_d"/>
326 <reg name="d2" bitsize="64" type="neon_d"/>
327 <reg name="d3" bitsize="64" type="neon_d"/>
328 <reg name="d4" bitsize="64" type="neon_d"/>
329 <reg name="d5" bitsize="64" type="neon_d"/>
330 <reg name="d6" bitsize="64" type="neon_d"/>
331 <reg name="d7" bitsize="64" type="neon_d"/>
332 <reg name="d8" bitsize="64" type="neon_d"/>
333 <reg name="d9" bitsize="64" type="neon_d"/>
334 <reg name="d10" bitsize="64" type="neon_d"/>
335 <reg name="d11" bitsize="64" type="neon_d"/>
336 <reg name="d12" bitsize="64" type="neon_d"/>
337 <reg name="d13" bitsize="64" type="neon_d"/>
338 <reg name="d14" bitsize="64" type="neon_d"/>
339 <reg name="d15" bitsize="64" type="neon_d"/>
340 <reg name="d16" bitsize="64" type="neon_d"/>
341 <reg name="d17" bitsize="64" type="neon_d"/>
342 <reg name="d18" bitsize="64" type="neon_d"/>
343 <reg name="d19" bitsize="64" type="neon_d"/>
344 <reg name="d20" bitsize="64" type="neon_d"/>
345 <reg name="d21" bitsize="64" type="neon_d"/>
346 <reg name="d22" bitsize="64" type="neon_d"/>
347 <reg name="d23" bitsize="64" type="neon_d"/>
348 <reg name="d24" bitsize="64" type="neon_d"/>
349 <reg name="d25" bitsize="64" type="neon_d"/>
350 <reg name="d26" bitsize="64" type="neon_d"/>
351 <reg name="d27" bitsize="64" type="neon_d"/>
352 <reg name="d28" bitsize="64" type="neon_d"/>
353 <reg name="d29" bitsize="64" type="neon_d"/>
354 <reg name="d30" bitsize="64" type="neon_d"/>
355 <reg name="d31" bitsize="64" type="neon_d"/>
356
357 <reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
358 <reg name="q1" bitsize="128" type="neon_q"/>
359 <reg name="q2" bitsize="128" type="neon_q"/>
360 <reg name="q3" bitsize="128" type="neon_q"/>
361 <reg name="q4" bitsize="128" type="neon_q"/>
362 <reg name="q5" bitsize="128" type="neon_q"/>
363 <reg name="q6" bitsize="128" type="neon_q"/>
364 <reg name="q7" bitsize="128" type="neon_q"/>
365 <reg name="q8" bitsize="128" type="neon_q"/>
366 <reg name="q9" bitsize="128" type="neon_q"/>
367 <reg name="q10" bitsize="128" type="neon_q"/>
368 <reg name="q10" bitsize="128" type="neon_q"/>
369 <reg name="q12" bitsize="128" type="neon_q"/>
370 <reg name="q13" bitsize="128" type="neon_q"/>
371 <reg name="q14" bitsize="128" type="neon_q"/>
372 <reg name="q15" bitsize="128" type="neon_q"/>
373
374 <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
375 </feature>
376</target>)";
377
378 return target_xml;
379}
380
381std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
382 if (!thread) {
383 return "";
384 }
385
386 const auto& context{thread->GetContext32()};
387 const auto& gprs{context.cpu_registers};
388 const auto& fprs{context.extension_registers};
389
390 if (id <= PC_REGISTER) {
391 return ValueToHex(gprs[id]);
392 } else if (id == CPSR_REGISTER) {
393 return ValueToHex(context.cpsr);
394 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
395 const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
396 return ValueToHex(dN);
397 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
398 const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
399 return ValueToHex(qN);
400 } else if (id == FPSCR_REGISTER) {
401 return ValueToHex(context.fpscr);
402 } else {
403 return "";
404 }
405}
406
407void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
408 if (!thread) {
409 return;
410 }
411
412 auto& context{thread->GetContext32()};
413 auto& fprs{context.extension_registers};
414
415 if (id <= PC_REGISTER) {
416 context.cpu_registers[id] = HexToValue<u32>(value);
417 } else if (id == CPSR_REGISTER) {
418 context.cpsr = HexToValue<u32>(value);
419 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
420 PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
421 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
422 PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
423 } else if (id == FPSCR_REGISTER) {
424 context.fpscr = HexToValue<u32>(value);
425 }
426}
427
428std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
429 std::string output;
430
431 for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
432 const bool gpr{reg <= PC_REGISTER};
433 const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
434 const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
435
436 if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
437 continue;
438 }
439
440 output += RegRead(thread, reg);
441 }
442
443 return output;
444}
445
446void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
447 for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
448 const bool gpr{reg <= PC_REGISTER};
449 const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
450 const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
451
452 if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
453 RegWrite(thread, reg, register_data.substr(i, 8));
454 i += 8;
455 } else if (dfpr) {
456 RegWrite(thread, reg, register_data.substr(i, 16));
457 i += 16;
458 } else if (qfpr) {
459 RegWrite(thread, reg, register_data.substr(i, 32));
460 i += 32;
461 }
462
463 if (reg == PC_REGISTER) {
464 reg = CPSR_REGISTER - 1;
465 } else if (reg == CPSR_REGISTER) {
466 reg = D0_REGISTER - 1;
467 }
468 }
469}
470
471std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
472 return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
473 RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
474 LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
475}
476
477u32 GDBStubA32::BreakpointInstruction() const {
478 // A32: trap
479 // T32: trap + b #4
480 return 0xe7ffdefe;
481}
482
483} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h
new file mode 100644
index 000000000..2540d6456
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.h
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9
10namespace Kernel {
11class KThread;
12}
13
14namespace Core {
15
16class GDBStubArch {
17public:
18 virtual ~GDBStubArch() = default;
19 virtual std::string GetTargetXML() const = 0;
20 virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
21 virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
22 virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
23 virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
24 virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
25 virtual u32 BreakpointInstruction() const = 0;
26};
27
28class GDBStubA64 final : public GDBStubArch {
29public:
30 std::string GetTargetXML() const override;
31 std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
32 void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
33 std::string ReadRegisters(const Kernel::KThread* thread) const override;
34 void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
35 std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
36 u32 BreakpointInstruction() const override;
37
38private:
39 static constexpr u32 LR_REGISTER = 30;
40 static constexpr u32 SP_REGISTER = 31;
41 static constexpr u32 PC_REGISTER = 32;
42 static constexpr u32 PSTATE_REGISTER = 33;
43 static constexpr u32 Q0_REGISTER = 34;
44 static constexpr u32 FPSR_REGISTER = 66;
45 static constexpr u32 FPCR_REGISTER = 67;
46};
47
48class GDBStubA32 final : public GDBStubArch {
49public:
50 std::string GetTargetXML() const override;
51 std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
52 void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
53 std::string ReadRegisters(const Kernel::KThread* thread) const override;
54 void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
55 std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
56 u32 BreakpointInstruction() const override;
57
58private:
59 static constexpr u32 SP_REGISTER = 13;
60 static constexpr u32 LR_REGISTER = 14;
61 static constexpr u32 PC_REGISTER = 15;
62 static constexpr u32 CPSR_REGISTER = 25;
63 static constexpr u32 D0_REGISTER = 32;
64 static constexpr u32 Q0_REGISTER = 64;
65 static constexpr u32 FPSCR_REGISTER = 80;
66};
67
68} // namespace Core
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 93f784418..78e56bbbd 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -419,7 +419,7 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
419 Core::Crypto::Mode::ECB); 419 Core::Crypto::Mode::ECB);
420 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); 420 cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
421 421
422 Core::Crypto::Key128 out; 422 Core::Crypto::Key128 out{};
423 if (type == NCASectionCryptoType::XTS) { 423 if (type == NCASectionCryptoType::XTS) {
424 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); 424 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
425 } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) { 425 } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) {
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index d4c0a974a..2735d053b 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -50,7 +50,7 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
50 low = mid + 1; 50 low = mid + 1;
51 } 51 }
52 } 52 }
53 UNREACHABLE_MSG("Offset could not be found in BKTR block."); 53 ASSERT_MSG(false, "Offset could not be found in BKTR block.");
54 return {0, 0}; 54 return {0, 0};
55} 55}
56} // Anonymous namespace 56} // Anonymous namespace
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 2eaac73ef..878d832c2 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -108,7 +108,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
108 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 108 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
109 return ContentRecordType::HtmlDocument; 109 return ContentRecordType::HtmlDocument;
110 default: 110 default:
111 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type); 111 ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type);
112 return ContentRecordType{}; 112 return ContentRecordType{};
113 } 113 }
114} 114}
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index e42d7c9f6..cc0076238 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -144,7 +144,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
144 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); 144 LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
145 } 145 }
146 } else { 146 } else {
147 UNREACHABLE(); 147 ASSERT(false);
148 return nullptr; 148 return nullptr;
149 } 149 }
150 150
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index 0ff2a338e..6c230f619 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -65,7 +65,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
65 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld); 65 controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
66 controller->Connect(true); 66 controller->Connect(true);
67 } else { 67 } else {
68 UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); 68 ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
69 } 69 }
70 } 70 }
71 71
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index fd220ccb5..aac45907d 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -27,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
27 // We can't use mouse as touch if native mouse is enabled 27 // We can't use mouse as touch if native mouse is enabled
28 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; 28 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
29 } 29 }
30 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"}; 30
31 touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"}; 31 touch_params[index++] =
32 Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
33 touch_params[index++] =
34 Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
35 touch_params[index++] =
36 Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
37 touch_params[index++] =
38 Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
32 touch_params[index++] = 39 touch_params[index++] =
33 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"}; 40 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
34 touch_params[index++] = 41 touch_params[index++] =
35 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"}; 42 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
36 43
37 const auto button_index = 44 const auto button_index =
38 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); 45 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index ba1dcd171..bd2384515 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -884,18 +884,42 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
884} 884}
885 885
886bool EmulatedController::TestVibration(std::size_t device_index) { 886bool EmulatedController::TestVibration(std::size_t device_index) {
887 static constexpr VibrationValue test_vibration = { 887 if (device_index >= output_devices.size()) {
888 return false;
889 }
890 if (!output_devices[device_index]) {
891 return false;
892 }
893
894 const auto player_index = NpadIdTypeToIndex(npad_id_type);
895 const auto& player = Settings::values.players.GetValue()[player_index];
896
897 if (!player.vibration_enabled) {
898 return false;
899 }
900
901 const Common::Input::VibrationStatus test_vibration = {
888 .low_amplitude = 0.001f, 902 .low_amplitude = 0.001f,
889 .low_frequency = 160.0f, 903 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
890 .high_amplitude = 0.001f, 904 .high_amplitude = 0.001f,
891 .high_frequency = 320.0f, 905 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
906 .type = Common::Input::VibrationAmplificationType::Test,
907 };
908
909 const Common::Input::VibrationStatus zero_vibration = {
910 .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
911 .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
912 .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
913 .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
914 .type = Common::Input::VibrationAmplificationType::Test,
892 }; 915 };
893 916
894 // Send a slight vibration to test for rumble support 917 // Send a slight vibration to test for rumble support
895 SetVibration(device_index, test_vibration); 918 output_devices[device_index]->SetVibration(test_vibration);
896 919
897 // Stop any vibration and return the result 920 // Stop any vibration and return the result
898 return SetVibration(device_index, DEFAULT_VIBRATION_VALUE); 921 return output_devices[device_index]->SetVibration(zero_vibration) ==
922 Common::Input::VibrationError::None;
899} 923}
900 924
901bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 925bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index 7eed52593..7d6373414 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -48,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
48 return handheld.get(); 48 return handheld.get();
49 case NpadIdType::Invalid: 49 case NpadIdType::Invalid:
50 default: 50 default:
51 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); 51 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
52 return nullptr; 52 return nullptr;
53 } 53 }
54} 54}
@@ -77,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
77 return handheld.get(); 77 return handheld.get();
78 case NpadIdType::Invalid: 78 case NpadIdType::Invalid:
79 default: 79 default:
80 UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type); 80 ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
81 return nullptr; 81 return nullptr;
82 } 82 }
83} 83}
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 26ec1091b..9f76f9bcb 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -498,6 +498,49 @@ struct SixAxisSensorFusionParameters {
498static_assert(sizeof(SixAxisSensorFusionParameters) == 8, 498static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
499 "SixAxisSensorFusionParameters is an invalid size"); 499 "SixAxisSensorFusionParameters is an invalid size");
500 500
501// This is nn::hid::server::SixAxisSensorProperties
502struct SixAxisSensorProperties {
503 union {
504 u8 raw{};
505 BitField<0, 1, u8> is_newly_assigned;
506 BitField<1, 1, u8> is_firmware_update_available;
507 };
508};
509static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
510
511// This is nn::hid::SixAxisSensorCalibrationParameter
512struct SixAxisSensorCalibrationParameter {
513 std::array<u8, 0x744> unknown_data{};
514};
515static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
516 "SixAxisSensorCalibrationParameter is an invalid size");
517
518// This is nn::hid::SixAxisSensorIcInformation
519struct SixAxisSensorIcInformation {
520 f32 angular_rate{2000.0f}; // dps
521 std::array<f32, 6> unknown_gyro_data1{
522 -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
523 }; // dps
524 std::array<f32, 9> unknown_gyro_data2{
525 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
526 };
527 std::array<f32, 9> unknown_gyro_data3{
528 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
529 };
530 f32 acceleration_range{8.0f}; // g force
531 std::array<f32, 6> unknown_accel_data1{
532 -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
533 }; // g force
534 std::array<f32, 9> unknown_accel_data2{
535 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
536 };
537 std::array<f32, 9> unknown_accel_data3{
538 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
539 };
540};
541static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
542 "SixAxisSensorIcInformation is an invalid size");
543
501// This is nn::hid::VibrationDeviceHandle 544// This is nn::hid::VibrationDeviceHandle
502struct VibrationDeviceHandle { 545struct VibrationDeviceHandle {
503 NpadStyleIndex npad_type{NpadStyleIndex::None}; 546 NpadStyleIndex npad_type{NpadStyleIndex::None};
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 3c26260f3..18d9f042d 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -1,6 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
4#include <random> 5#include <random>
5 6
6#include "common/input.h" 7#include "common/input.h"
@@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
196 x = std::clamp(x, 0.0f, 1.0f); 197 x = std::clamp(x, 0.0f, 1.0f);
197 y = std::clamp(y, 0.0f, 1.0f); 198 y = std::clamp(y, 0.0f, 1.0f);
198 199
200 // Limit id to maximum number of fingers
201 status.id = std::clamp(status.id, 0, 16);
202
199 if (status.pressed.inverted) { 203 if (status.pressed.inverted) {
200 status.pressed.value = !status.pressed.value; 204 status.pressed.value = !status.pressed.value;
201 } 205 }
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index a427cbc93..0ddc8df9e 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -141,7 +141,7 @@ public:
141 if (index < DomainHandlerCount()) { 141 if (index < DomainHandlerCount()) {
142 domain_handlers[index] = nullptr; 142 domain_handlers[index] = nullptr;
143 } else { 143 } else {
144 UNREACHABLE_MSG("Unexpected handler index {}", index); 144 ASSERT_MSG(false, "Unexpected handler index {}", index);
145 } 145 }
146 } 146 }
147 147
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 34a8be052..9b6b284d0 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -244,7 +244,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
244 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) 244 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
245 // If we somehow get an invalid type, abort. 245 // If we somehow get an invalid type, abort.
246 default: 246 default:
247 UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]); 247 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
248 } 248 }
249 249
250 // If we've hit the end of a gap, free it. 250 // If we've hit the end of a gap, free it.
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
index e46e0d848..5fa19d386 100644
--- a/src/core/hle/kernel/k_address_arbiter.h
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -35,7 +35,7 @@ public:
35 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: 35 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
36 return SignalAndModifyByWaitingCountIfEqual(addr, value, count); 36 return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
37 } 37 }
38 UNREACHABLE(); 38 ASSERT(false);
39 return ResultUnknown; 39 return ResultUnknown;
40 } 40 }
41 41
@@ -49,7 +49,7 @@ public:
49 case Svc::ArbitrationType::WaitIfEqual: 49 case Svc::ArbitrationType::WaitIfEqual:
50 return WaitIfEqual(addr, value, timeout); 50 return WaitIfEqual(addr, value, timeout);
51 } 51 }
52 UNREACHABLE(); 52 ASSERT(false);
53 return ResultUnknown; 53 return ResultUnknown;
54 } 54 }
55 55
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp
index bc37cadda..3e612a207 100644
--- a/src/core/hle/kernel/k_address_space_info.cpp
+++ b/src/core/hle/kernel/k_address_space_info.cpp
@@ -84,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
84 ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); 84 ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
85 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address; 85 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
86 } 86 }
87 UNREACHABLE(); 87 ASSERT(false);
88 return 0; 88 return 0;
89} 89}
90 90
@@ -101,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
101 ASSERT(IsAllowed39BitType(type)); 101 ASSERT(IsAllowed39BitType(type));
102 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size; 102 return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
103 } 103 }
104 UNREACHABLE(); 104 ASSERT(false);
105 return 0; 105 return 0;
106} 106}
107 107
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index ea47fc600..2827763d5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -18,7 +18,7 @@ namespace Kernel {
18class KernelCore; 18class KernelCore;
19class KProcess; 19class KProcess;
20 20
21#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ 21#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
22 \ 22 \
23private: \ 23private: \
24 friend class ::Kernel::KClassTokenGenerator; \ 24 friend class ::Kernel::KClassTokenGenerator; \
@@ -40,16 +40,19 @@ public:
40 static constexpr const char* GetStaticTypeName() { \ 40 static constexpr const char* GetStaticTypeName() { \
41 return TypeName; \ 41 return TypeName; \
42 } \ 42 } \
43 virtual TypeObj GetTypeObj() const { \ 43 virtual TypeObj GetTypeObj() ATTRIBUTE { \
44 return GetStaticTypeObj(); \ 44 return GetStaticTypeObj(); \
45 } \ 45 } \
46 virtual const char* GetTypeName() const { \ 46 virtual const char* GetTypeName() ATTRIBUTE { \
47 return GetStaticTypeName(); \ 47 return GetStaticTypeName(); \
48 } \ 48 } \
49 \ 49 \
50private: \ 50private: \
51 constexpr bool operator!=(const TypeObj& rhs) 51 constexpr bool operator!=(const TypeObj& rhs)
52 52
53#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
54 KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
55
53class KAutoObject { 56class KAutoObject {
54protected: 57protected:
55 class TypeObj { 58 class TypeObj {
@@ -82,7 +85,7 @@ protected:
82 }; 85 };
83 86
84private: 87private:
85 KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); 88 KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
86 89
87public: 90public:
88 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { 91 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index be9e3c357..c9001ae3d 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -49,6 +49,7 @@ private:
49 } 49 }
50 } 50 }
51 } 51 }
52 UNREACHABLE();
52 }(); 53 }();
53 54
54 template <typename T> 55 template <typename T>
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index fd3cbfd94..4ae40ec8e 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -27,23 +27,18 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
27 auto& page_table = m_owner->PageTable(); 27 auto& page_table = m_owner->PageTable();
28 28
29 // Construct the page group. 29 // Construct the page group.
30 m_page_group = 30 m_page_group = {};
31 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
32 31
33 // Lock the memory. 32 // Lock the memory.
34 R_TRY(page_table.LockForCodeMemory(addr, size)) 33 R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
35 34
36 // Clear the memory. 35 // Clear the memory.
37 // 36 for (const auto& block : m_page_group.Nodes()) {
38 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within 37 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
39 // guest memory, and is not specifically required if the guest program is correctly 38 }
40 // written, so disable until this is further investigated.
41 //
42 // for (const auto& block : m_page_group.Nodes()) {
43 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
44 // }
45 39
46 // Set remaining tracking members. 40 // Set remaining tracking members.
41 m_owner->Open();
47 m_address = addr; 42 m_address = addr;
48 m_is_initialized = true; 43 m_is_initialized = true;
49 m_is_owner_mapped = false; 44 m_is_owner_mapped = false;
@@ -57,8 +52,14 @@ void KCodeMemory::Finalize() {
57 // Unlock. 52 // Unlock.
58 if (!m_is_mapped && !m_is_owner_mapped) { 53 if (!m_is_mapped && !m_is_owner_mapped) {
59 const size_t size = m_page_group.GetNumPages() * PageSize; 54 const size_t size = m_page_group.GetNumPages() * PageSize;
60 m_owner->PageTable().UnlockForCodeMemory(m_address, size); 55 m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
61 } 56 }
57
58 // Close the page group.
59 m_page_group = {};
60
61 // Close our reference to our owner.
62 m_owner->Close();
62} 63}
63 64
64ResultCode KCodeMemory::Map(VAddr address, size_t size) { 65ResultCode KCodeMemory::Map(VAddr address, size_t size) {
@@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
118 k_perm = KMemoryPermission::UserReadExecute; 119 k_perm = KMemoryPermission::UserReadExecute;
119 break; 120 break;
120 default: 121 default:
121 break; 122 // Already validated by ControlCodeMemory svc
123 UNREACHABLE();
122 } 124 }
123 125
124 // Map the memory. 126 // Map the memory.
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index a55db3088..58e540f31 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -29,7 +29,7 @@ constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) { 29 } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
30 return KMemoryManager::Pool::SystemNonSecure; 30 return KMemoryManager::Pool::SystemNonSecure;
31 } else { 31 } else {
32 UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool"); 32 ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
33 return {}; 33 return {};
34 } 34 }
35} 35}
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index b38ef333b..504e22cb9 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -35,7 +35,7 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
35 case FileSys::ProgramAddressSpaceType::Is39Bit: 35 case FileSys::ProgramAddressSpaceType::Is39Bit:
36 return 39; 36 return 39;
37 default: 37 default:
38 UNREACHABLE(); 38 ASSERT(false);
39 return {}; 39 return {};
40 } 40 }
41} 41}
@@ -128,7 +128,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
128 const std::size_t needed_size{ 128 const std::size_t needed_size{
129 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; 129 (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
130 if (alloc_size < needed_size) { 130 if (alloc_size < needed_size) {
131 UNREACHABLE(); 131 ASSERT(false);
132 return ResultOutOfMemory; 132 return ResultOutOfMemory;
133 } 133 }
134 134
@@ -542,6 +542,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
542 return ResultSuccess; 542 return ResultSuccess;
543} 543}
544 544
545bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
546 ASSERT(this->IsLockedByCurrentThread());
547
548 const size_t size = num_pages * PageSize;
549 const auto& pg = pg_ll.Nodes();
550 const auto& memory_layout = system.Kernel().MemoryLayout();
551
552 // Empty groups are necessarily invalid.
553 if (pg.empty()) {
554 return false;
555 }
556
557 // We're going to validate that the group we'd expect is the group we see.
558 auto cur_it = pg.begin();
559 PAddr cur_block_address = cur_it->GetAddress();
560 size_t cur_block_pages = cur_it->GetNumPages();
561
562 auto UpdateCurrentIterator = [&]() {
563 if (cur_block_pages == 0) {
564 if ((++cur_it) == pg.end()) {
565 return false;
566 }
567
568 cur_block_address = cur_it->GetAddress();
569 cur_block_pages = cur_it->GetNumPages();
570 }
571 return true;
572 };
573
574 // Begin traversal.
575 Common::PageTable::TraversalContext context;
576 Common::PageTable::TraversalEntry next_entry;
577 if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
578 return false;
579 }
580
581 // Prepare tracking variables.
582 PAddr cur_addr = next_entry.phys_addr;
583 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
584 size_t tot_size = cur_size;
585
586 // Iterate, comparing expected to actual.
587 while (tot_size < size) {
588 if (!page_table_impl.ContinueTraversal(next_entry, context)) {
589 return false;
590 }
591
592 if (next_entry.phys_addr != (cur_addr + cur_size)) {
593 const size_t cur_pages = cur_size / PageSize;
594
595 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
596 return false;
597 }
598
599 if (!UpdateCurrentIterator()) {
600 return false;
601 }
602
603 if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
604 return false;
605 }
606
607 cur_block_address += cur_size;
608 cur_block_pages -= cur_pages;
609 cur_addr = next_entry.phys_addr;
610 cur_size = next_entry.block_size;
611 } else {
612 cur_size += next_entry.block_size;
613 }
614
615 tot_size += next_entry.block_size;
616 }
617
618 // Ensure we compare the right amount for the last block.
619 if (tot_size > size) {
620 cur_size -= (tot_size - size);
621 }
622
623 if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
624 return false;
625 }
626
627 if (!UpdateCurrentIterator()) {
628 return false;
629 }
630
631 return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
632}
633
545ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 634ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
546 KPageTable& src_page_table, VAddr src_addr) { 635 KPageTable& src_page_table, VAddr src_addr) {
547 KScopedLightLock lk(general_lock); 636 KScopedLightLock lk(general_lock);
@@ -1341,7 +1430,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1341 new_state = KMemoryState::AliasCodeData; 1430 new_state = KMemoryState::AliasCodeData;
1342 break; 1431 break;
1343 default: 1432 default:
1344 UNREACHABLE(); 1433 ASSERT(false);
1345 } 1434 }
1346 } 1435 }
1347 1436
@@ -1687,22 +1776,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1687 return ResultSuccess; 1776 return ResultSuccess;
1688} 1777}
1689 1778
1690ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1779ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
1691 return this->LockMemoryAndOpen( 1780 return this->LockMemoryAndOpen(
1692 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, 1781 out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1693 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, 1782 KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
1694 KMemoryAttribute::All, KMemoryAttribute::None, 1783 KMemoryAttribute::None,
1695 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped | 1784 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1696 KMemoryPermission::KernelReadWrite), 1785 KMemoryPermission::KernelReadWrite),
1697 KMemoryAttribute::Locked); 1786 KMemoryAttribute::Locked);
1698} 1787}
1699 1788
1700ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1789ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
1701 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory, 1790 const KPageLinkedList& pg) {
1702 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, 1791 return this->UnlockMemory(
1703 KMemoryPermission::None, KMemoryAttribute::All, 1792 addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
1704 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, 1793 KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
1705 KMemoryAttribute::Locked, nullptr); 1794 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
1706} 1795}
1707 1796
1708ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1797ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1734,9 +1823,7 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
1734 VAddr addr{start}; 1823 VAddr addr{start};
1735 while (addr < start + (num_pages * PageSize)) { 1824 while (addr < start + (num_pages * PageSize)) {
1736 const PAddr paddr{GetPhysicalAddr(addr)}; 1825 const PAddr paddr{GetPhysicalAddr(addr)};
1737 if (!paddr) { 1826 ASSERT(paddr != 0);
1738 UNREACHABLE();
1739 }
1740 page_linked_list.AddBlock(paddr, 1); 1827 page_linked_list.AddBlock(paddr, 1);
1741 addr += PageSize; 1828 addr += PageSize;
1742 } 1829 }
@@ -1767,7 +1854,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
1767 system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); 1854 system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
1768 break; 1855 break;
1769 default: 1856 default:
1770 UNREACHABLE(); 1857 ASSERT(false);
1771 } 1858 }
1772 1859
1773 addr += size; 1860 addr += size;
@@ -1798,7 +1885,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
1798 case OperationType::ChangePermissionsAndRefresh: 1885 case OperationType::ChangePermissionsAndRefresh:
1799 break; 1886 break;
1800 default: 1887 default:
1801 UNREACHABLE(); 1888 ASSERT(false);
1802 } 1889 }
1803 return ResultSuccess; 1890 return ResultSuccess;
1804} 1891}
@@ -1835,7 +1922,6 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
1835 return code_region_start; 1922 return code_region_start;
1836 default: 1923 default:
1837 UNREACHABLE(); 1924 UNREACHABLE();
1838 return {};
1839 } 1925 }
1840} 1926}
1841 1927
@@ -1871,7 +1957,6 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
1871 return code_region_end - code_region_start; 1957 return code_region_end - code_region_start;
1872 default: 1958 default:
1873 UNREACHABLE(); 1959 UNREACHABLE();
1874 return {};
1875 } 1960 }
1876} 1961}
1877 1962
@@ -2125,7 +2210,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_
2125 2210
2126 // Check the page group. 2211 // Check the page group.
2127 if (pg != nullptr) { 2212 if (pg != nullptr) {
2128 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!"); 2213 R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
2129 } 2214 }
2130 2215
2131 // Decide on new perm and attr. 2216 // Decide on new perm and attr.
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 52a93ce86..6312eb682 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -72,8 +72,8 @@ public:
72 KMemoryPermission perm, PAddr map_addr = 0); 72 KMemoryPermission perm, PAddr map_addr = 0);
73 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size); 73 ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
74 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 74 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
75 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 75 ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
76 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 76 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
77 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages, 77 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
78 KMemoryState state_mask, KMemoryState state, 78 KMemoryState state_mask, KMemoryState state,
79 KMemoryPermission perm_mask, KMemoryPermission perm, 79 KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -178,6 +178,7 @@ private:
178 const KPageLinkedList* pg); 178 const KPageLinkedList* pg);
179 179
180 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages); 180 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
181 bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
181 182
182 bool IsLockedByCurrentThread() const { 183 bool IsLockedByCurrentThread() const {
183 return general_lock.IsLockedByCurrentThread(); 184 return general_lock.IsLockedByCurrentThread();
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index a31861cdb..51c2cd1ef 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -60,7 +60,7 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) { 60 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
61 session_ptr->ClientConnected(server.AcceptSession()); 61 session_ptr->ClientConnected(server.AcceptSession());
62 } else { 62 } else {
63 UNREACHABLE(); 63 ASSERT(false);
64 } 64 }
65 65
66 return ResultSuccess; 66 return ResultSuccess;
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 490e31fc7..8c79b4f0f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -64,6 +64,10 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
64 { 64 {
65 KScopedSchedulerLock lock{kernel}; 65 KScopedSchedulerLock lock{kernel};
66 thread->SetState(ThreadState::Runnable); 66 thread->SetState(ThreadState::Runnable);
67
68 if (system.DebuggerEnabled()) {
69 thread->RequestSuspend(SuspendType::Debug);
70 }
67 } 71 }
68} 72}
69} // Anonymous namespace 73} // Anonymous namespace
@@ -346,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
346 break; 350 break;
347 351
348 default: 352 default:
349 UNREACHABLE(); 353 ASSERT(false);
350 } 354 }
351 355
352 // Create TLS region 356 // Create TLS region
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 7e39f6d50..60f8ed470 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -97,13 +97,13 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
97 "object_id {} is too big! This probably means a recent service call " 97 "object_id {} is too big! This probably means a recent service call "
98 "to {} needed to return a new interface!", 98 "to {} needed to return a new interface!",
99 object_id, name); 99 object_id, name);
100 UNREACHABLE(); 100 ASSERT(false);
101 return ResultSuccess; // Ignore error if asserts are off 101 return ResultSuccess; // Ignore error if asserts are off
102 } 102 }
103 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) { 103 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
104 return strong_ptr->HandleSyncRequest(*this, context); 104 return strong_ptr->HandleSyncRequest(*this, context);
105 } else { 105 } else {
106 UNREACHABLE(); 106 ASSERT(false);
107 return ResultSuccess; 107 return ResultSuccess;
108 } 108 }
109 109
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index ab9ce6a86..ea2160099 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -133,7 +133,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
133 UNIMPLEMENTED(); 133 UNIMPLEMENTED();
134 break; 134 break;
135 default: 135 default:
136 UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); 136 ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
137 break; 137 break;
138 } 138 }
139 thread_type = type; 139 thread_type = type;
@@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
198 resource_limit_release_hint = false; 198 resource_limit_release_hint = false;
199 cpu_time = 0; 199 cpu_time = 0;
200 200
201 // Set debug context.
202 stack_top = user_stack_top;
203 argument = arg;
204
201 // Clear our stack parameters. 205 // Clear our stack parameters.
202 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, 206 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
203 sizeof(StackParameters)); 207 sizeof(StackParameters));
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index b55a922ab..f4d83f99a 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -100,6 +100,12 @@ enum class ThreadWaitReasonForDebugging : u32 {
100 Suspended, ///< Thread is waiting due to process suspension 100 Suspended, ///< Thread is waiting due to process suspension
101}; 101};
102 102
103enum class StepState : u32 {
104 NotStepping, ///< Thread is not currently stepping
105 StepPending, ///< Thread will step when next scheduled
106 StepPerformed, ///< Thread has stepped, waiting to be scheduled again
107};
108
103[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); 109[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
104[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); 110[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
105[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); 111[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -267,6 +273,14 @@ public:
267 273
268 void SetState(ThreadState state); 274 void SetState(ThreadState state);
269 275
276 [[nodiscard]] StepState GetStepState() const {
277 return step_state;
278 }
279
280 void SetStepState(StepState state) {
281 step_state = state;
282 }
283
270 [[nodiscard]] s64 GetLastScheduledTick() const { 284 [[nodiscard]] s64 GetLastScheduledTick() const {
271 return last_scheduled_tick; 285 return last_scheduled_tick;
272 } 286 }
@@ -646,6 +660,14 @@ public:
646 void IfDummyThreadTryWait(); 660 void IfDummyThreadTryWait();
647 void IfDummyThreadEndWait(); 661 void IfDummyThreadEndWait();
648 662
663 [[nodiscard]] uintptr_t GetArgument() const {
664 return argument;
665 }
666
667 [[nodiscard]] VAddr GetUserStackTop() const {
668 return stack_top;
669 }
670
649private: 671private:
650 static constexpr size_t PriorityInheritanceCountMax = 10; 672 static constexpr size_t PriorityInheritanceCountMax = 10;
651 union SyncObjectBuffer { 673 union SyncObjectBuffer {
@@ -769,6 +791,7 @@ private:
769 std::shared_ptr<Common::Fiber> host_context{}; 791 std::shared_ptr<Common::Fiber> host_context{};
770 bool is_single_core{}; 792 bool is_single_core{};
771 ThreadType thread_type{}; 793 ThreadType thread_type{};
794 StepState step_state{};
772 std::mutex dummy_wait_lock; 795 std::mutex dummy_wait_lock;
773 std::condition_variable dummy_wait_cv; 796 std::condition_variable dummy_wait_cv;
774 797
@@ -776,6 +799,8 @@ private:
776 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 799 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
777 VAddr mutex_wait_address_for_debugging{}; 800 VAddr mutex_wait_address_for_debugging{};
778 ThreadWaitReasonForDebugging wait_reason_for_debugging{}; 801 ThreadWaitReasonForDebugging wait_reason_for_debugging{};
802 uintptr_t argument;
803 VAddr stack_top;
779 804
780public: 805public:
781 using ConditionVariableThreadTreeType = ConditionVariableThreadTree; 806 using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92f6d8c49..b2c4f12b4 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -212,7 +212,9 @@ struct KernelCore::Impl {
212 system_resource_limit = KResourceLimit::Create(system.Kernel()); 212 system_resource_limit = KResourceLimit::Create(system.Kernel());
213 system_resource_limit->Initialize(&core_timing); 213 system_resource_limit->Initialize(&core_timing);
214 214
215 const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes(); 215 const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
216 const auto total_size{sizes.first};
217 const auto kernel_size{sizes.second};
216 218
217 // If setting the default system values fails, then something seriously wrong has occurred. 219 // If setting the default system values fails, then something seriously wrong has occurred.
218 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) 220 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -252,6 +254,7 @@ struct KernelCore::Impl {
252 core_id) 254 core_id)
253 .IsSuccess()); 255 .IsSuccess());
254 suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id)); 256 suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
257 suspend_threads[core_id]->DisableDispatch();
255 } 258 }
256 } 259 }
257 260
@@ -1073,9 +1076,6 @@ void KernelCore::Suspend(bool in_suspention) {
1073 impl->suspend_threads[core_id]->SetState(state); 1076 impl->suspend_threads[core_id]->SetState(state);
1074 impl->suspend_threads[core_id]->SetWaitReasonForDebugging( 1077 impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
1075 ThreadWaitReasonForDebugging::Suspended); 1078 ThreadWaitReasonForDebugging::Suspended);
1076 if (!should_suspend) {
1077 impl->suspend_threads[core_id]->DisableDispatch();
1078 }
1079 } 1079 }
1080 } 1080 }
1081} 1081}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 66e0ce2d0..4f0a44363 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -15,6 +15,7 @@
15#include "common/scope_exit.h" 15#include "common/scope_exit.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
18#include "core/hle/kernel/k_client_port.h" 19#include "core/hle/kernel/k_client_port.h"
19#include "core/hle/kernel/k_client_session.h" 20#include "core/hle/kernel/k_client_session.h"
20#include "core/hle/kernel/k_code_memory.h" 21#include "core/hle/kernel/k_code_memory.h"
@@ -627,6 +628,12 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
627 const auto thread_processor_id = current_thread->GetActiveCore(); 628 const auto thread_processor_id = current_thread->GetActiveCore();
628 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); 629 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
629 } 630 }
631
632 if (system.DebuggerEnabled()) {
633 auto* thread = system.Kernel().GetCurrentEmuThread();
634 system.GetDebugger().NotifyThreadStopped(thread);
635 thread->RequestSuspend(Kernel::SuspendType::Debug);
636 }
630} 637}
631 638
632static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { 639static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
@@ -1876,7 +1883,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
1876 KScheduler::YieldToAnyThread(kernel); 1883 KScheduler::YieldToAnyThread(kernel);
1877 } else { 1884 } else {
1878 // Nintendo does nothing at all if an otherwise invalid value is passed. 1885 // Nintendo does nothing at all if an otherwise invalid value is passed.
1879 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1886 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1880 } 1887 }
1881} 1888}
1882 1889
@@ -2982,7 +2989,6 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
2982} 2989}
2983 2990
2984void Call(Core::System& system, u32 immediate) { 2991void Call(Core::System& system, u32 immediate) {
2985 system.ExitDynarmicProfile();
2986 auto& kernel = system.Kernel(); 2992 auto& kernel = system.Kernel();
2987 kernel.EnterSVCProfile(); 2993 kernel.EnterSVCProfile();
2988 2994
@@ -3007,8 +3013,6 @@ void Call(Core::System& system, u32 immediate) {
3007 auto* host_context = thread->GetHostContext().get(); 3013 auto* host_context = thread->GetHostContext().get();
3008 host_context->Rewind(); 3014 host_context->Rewind();
3009 } 3015 }
3010
3011 system.EnterDynarmicProfile();
3012} 3016}
3013 3017
3014} // namespace Kernel::Svc 3018} // namespace Kernel::Svc
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index 655f2e936..0a5603d18 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -178,7 +178,7 @@ ResultCode Controller::GetStatus() const {
178} 178}
179 179
180void Controller::ExecuteInteractive() { 180void Controller::ExecuteInteractive() {
181 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 181 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
182} 182}
183 183
184void Controller::Execute() { 184void Controller::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index 911b2c229..0b87c60b9 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -156,7 +156,7 @@ ResultCode Error::GetStatus() const {
156} 156}
157 157
158void Error::ExecuteInteractive() { 158void Error::ExecuteInteractive() {
159 UNREACHABLE_MSG("Unexpected interactive applet data!"); 159 ASSERT_MSG(false, "Unexpected interactive applet data!");
160} 160}
161 161
162void Error::Execute() { 162void Error::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index 3fe1a390a..41c002ef2 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -76,7 +76,7 @@ ResultCode Auth::GetStatus() const {
76} 76}
77 77
78void Auth::ExecuteInteractive() { 78void Auth::ExecuteInteractive() {
79 UNREACHABLE_MSG("Unexpected interactive applet data."); 79 ASSERT_MSG(false, "Unexpected interactive applet data.");
80} 80}
81 81
82void Auth::Execute() { 82void Auth::Execute() {
@@ -175,7 +175,7 @@ ResultCode PhotoViewer::GetStatus() const {
175} 175}
176 176
177void PhotoViewer::ExecuteInteractive() { 177void PhotoViewer::ExecuteInteractive() {
178 UNREACHABLE_MSG("Unexpected interactive applet data."); 178 ASSERT_MSG(false, "Unexpected interactive applet data.");
179} 179}
180 180
181void PhotoViewer::Execute() { 181void PhotoViewer::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
index 3acde1630..8d847c3f6 100644
--- a/src/core/hle/service/am/applets/applet_mii_edit.cpp
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -67,7 +67,7 @@ ResultCode MiiEdit::GetStatus() const {
67} 67}
68 68
69void MiiEdit::ExecuteInteractive() { 69void MiiEdit::ExecuteInteractive() {
70 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 70 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
71} 71}
72 72
73void MiiEdit::Execute() { 73void MiiEdit::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp
index fd16f2e49..02049fd9f 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/applets/applet_profile_select.cpp
@@ -44,7 +44,7 @@ ResultCode ProfileSelect::GetStatus() const {
44} 44}
45 45
46void ProfileSelect::ExecuteInteractive() { 46void ProfileSelect::ExecuteInteractive() {
47 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); 47 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
48} 48}
49 49
50void ProfileSelect::Execute() { 50void ProfileSelect::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index 7c21365e4..4116fbaa7 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -71,7 +71,7 @@ void SoftwareKeyboard::Initialize() {
71 InitializeBackground(applet_mode); 71 InitializeBackground(applet_mode);
72 break; 72 break;
73 default: 73 default:
74 UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); 74 ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
75 break; 75 break;
76 } 76 }
77} 77}
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 2aa4a00ad..7b3f77a51 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -279,7 +279,7 @@ void WebBrowser::Initialize() {
279 InitializeLobby(); 279 InitializeLobby();
280 break; 280 break;
281 default: 281 default:
282 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); 282 ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
283 break; 283 break;
284 } 284 }
285} 285}
@@ -320,7 +320,7 @@ void WebBrowser::Execute() {
320 ExecuteLobby(); 320 ExecuteLobby();
321 break; 321 break;
322 default: 322 default:
323 UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); 323 ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
324 WebBrowserExit(WebExitReason::EndButtonPressed); 324 WebBrowserExit(WebExitReason::EndButtonPressed);
325 break; 325 break;
326 } 326 }
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index ddfcba0f1..fae6e5aff 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -899,7 +899,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
899 case FileSys::SaveDataSpaceId::TemporaryStorage: 899 case FileSys::SaveDataSpaceId::TemporaryStorage:
900 case FileSys::SaveDataSpaceId::ProperSystem: 900 case FileSys::SaveDataSpaceId::ProperSystem:
901 case FileSys::SaveDataSpaceId::SafeMode: 901 case FileSys::SaveDataSpaceId::SafeMode:
902 UNREACHABLE(); 902 ASSERT(false);
903 } 903 }
904 904
905 auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()), 905 auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()),
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 3eae1ae35..32e0708ba 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
61 } 61 }
62 62
63 last_update_timestamp = shared_memory->gesture_lifo.timestamp; 63 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(gesture, time_difference);
64} 65}
65 66
66void Controller_Gesture::ReadTouchInput() { 67void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
94 return false; 95 return false;
95} 96}
96 97
97void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 98void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
98 GestureProperties& gesture,
99 f32 time_difference) { 99 f32 time_difference) {
100 GestureType type = GestureType::Idle; 100 GestureType type = GestureType::Idle;
101 GestureAttribute attributes{}; 101 GestureAttribute attributes{};
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index c62a341bf..0d6099ea0 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -107,8 +107,7 @@ private:
107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); 107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
108 108
109 // Updates the shared memory to the next state 109 // Updates the shared memory to the next state
110 void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, 110 void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
111 f32 time_difference);
112 111
113 // Initializes new gesture 112 // Initializes new gesture
114 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); 113 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 17f71beaf..ac5c38cc6 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -3,7 +3,9 @@
3 3
4#include <algorithm> 4#include <algorithm>
5#include <array> 5#include <array>
6#include <chrono>
6#include <cstring> 7#include <cstring>
8
7#include "common/assert.h" 9#include "common/assert.h"
8#include "common/bit_field.h" 10#include "common/bit_field.h"
9#include "common/common_types.h" 11#include "common/common_types.h"
@@ -54,11 +56,22 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle
54 return npad_id && npad_type && device_index; 56 return npad_id && npad_type && device_index;
55} 57}
56 58
57bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { 59ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
60 const Core::HID::SixAxisSensorHandle& device_handle) {
58 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); 61 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
59 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; 62 if (!npad_id) {
63 return InvalidNpadId;
64 }
60 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 65 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
61 return npad_id && npad_type && device_index; 66 if (!device_index) {
67 return NpadDeviceIndexOutOfRange;
68 }
69 // This doesn't get validated on nnsdk
70 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
71 if (!npad_type) {
72 return NpadInvalidHandle;
73 }
74 return ResultSuccess;
62} 75}
63 76
64Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 77Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
@@ -147,7 +160,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
147 shared_memory->system_properties.raw = 0; 160 shared_memory->system_properties.raw = 0;
148 switch (controller_type) { 161 switch (controller_type) {
149 case Core::HID::NpadStyleIndex::None: 162 case Core::HID::NpadStyleIndex::None:
150 UNREACHABLE(); 163 ASSERT(false);
151 break; 164 break;
152 case Core::HID::NpadStyleIndex::ProController: 165 case Core::HID::NpadStyleIndex::ProController:
153 shared_memory->style_tag.fullkey.Assign(1); 166 shared_memory->style_tag.fullkey.Assign(1);
@@ -156,6 +169,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
156 shared_memory->system_properties.use_plus.Assign(1); 169 shared_memory->system_properties.use_plus.Assign(1);
157 shared_memory->system_properties.use_minus.Assign(1); 170 shared_memory->system_properties.use_minus.Assign(1);
158 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; 171 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
172 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
159 break; 173 break;
160 case Core::HID::NpadStyleIndex::Handheld: 174 case Core::HID::NpadStyleIndex::Handheld:
161 shared_memory->style_tag.handheld.Assign(1); 175 shared_memory->style_tag.handheld.Assign(1);
@@ -168,16 +182,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
168 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; 182 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
169 shared_memory->applet_nfc_xcd.applet_footer.type = 183 shared_memory->applet_nfc_xcd.applet_footer.type =
170 AppletFooterUiType::HandheldJoyConLeftJoyConRight; 184 AppletFooterUiType::HandheldJoyConLeftJoyConRight;
185 shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
171 break; 186 break;
172 case Core::HID::NpadStyleIndex::JoyconDual: 187 case Core::HID::NpadStyleIndex::JoyconDual:
173 shared_memory->style_tag.joycon_dual.Assign(1); 188 shared_memory->style_tag.joycon_dual.Assign(1);
174 if (controller.is_dual_left_connected) { 189 if (controller.is_dual_left_connected) {
175 shared_memory->device_type.joycon_left.Assign(1); 190 shared_memory->device_type.joycon_left.Assign(1);
176 shared_memory->system_properties.use_minus.Assign(1); 191 shared_memory->system_properties.use_minus.Assign(1);
192 shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
177 } 193 }
178 if (controller.is_dual_right_connected) { 194 if (controller.is_dual_right_connected) {
179 shared_memory->device_type.joycon_right.Assign(1); 195 shared_memory->device_type.joycon_right.Assign(1);
180 shared_memory->system_properties.use_plus.Assign(1); 196 shared_memory->system_properties.use_plus.Assign(1);
197 shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
181 } 198 }
182 shared_memory->system_properties.use_directional_buttons.Assign(1); 199 shared_memory->system_properties.use_directional_buttons.Assign(1);
183 shared_memory->system_properties.is_vertical.Assign(1); 200 shared_memory->system_properties.is_vertical.Assign(1);
@@ -196,6 +213,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
196 shared_memory->system_properties.is_horizontal.Assign(1); 213 shared_memory->system_properties.is_horizontal.Assign(1);
197 shared_memory->system_properties.use_minus.Assign(1); 214 shared_memory->system_properties.use_minus.Assign(1);
198 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; 215 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
216 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
199 break; 217 break;
200 case Core::HID::NpadStyleIndex::JoyconRight: 218 case Core::HID::NpadStyleIndex::JoyconRight:
201 shared_memory->style_tag.joycon_right.Assign(1); 219 shared_memory->style_tag.joycon_right.Assign(1);
@@ -203,6 +221,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
203 shared_memory->system_properties.is_horizontal.Assign(1); 221 shared_memory->system_properties.is_horizontal.Assign(1);
204 shared_memory->system_properties.use_plus.Assign(1); 222 shared_memory->system_properties.use_plus.Assign(1);
205 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; 223 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
224 shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
206 break; 225 break;
207 case Core::HID::NpadStyleIndex::GameCube: 226 case Core::HID::NpadStyleIndex::GameCube:
208 shared_memory->style_tag.gamecube.Assign(1); 227 shared_memory->style_tag.gamecube.Assign(1);
@@ -213,6 +232,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
213 case Core::HID::NpadStyleIndex::Pokeball: 232 case Core::HID::NpadStyleIndex::Pokeball:
214 shared_memory->style_tag.palma.Assign(1); 233 shared_memory->style_tag.palma.Assign(1);
215 shared_memory->device_type.palma.Assign(1); 234 shared_memory->device_type.palma.Assign(1);
235 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
216 break; 236 break;
217 case Core::HID::NpadStyleIndex::NES: 237 case Core::HID::NpadStyleIndex::NES:
218 shared_memory->style_tag.lark.Assign(1); 238 shared_memory->style_tag.lark.Assign(1);
@@ -402,7 +422,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
402 libnx_state.connection_status.is_connected.Assign(1); 422 libnx_state.connection_status.is_connected.Assign(1);
403 switch (controller_type) { 423 switch (controller_type) {
404 case Core::HID::NpadStyleIndex::None: 424 case Core::HID::NpadStyleIndex::None:
405 UNREACHABLE(); 425 ASSERT(false);
406 break; 426 break;
407 case Core::HID::NpadStyleIndex::ProController: 427 case Core::HID::NpadStyleIndex::ProController:
408 case Core::HID::NpadStyleIndex::NES: 428 case Core::HID::NpadStyleIndex::NES:
@@ -529,6 +549,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
529 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; 549 auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
530 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; 550 auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
531 551
552 // Clear previous state
553 sixaxis_fullkey_state = {};
554 sixaxis_handheld_state = {};
555 sixaxis_dual_left_state = {};
556 sixaxis_dual_right_state = {};
557 sixaxis_left_lifo_state = {};
558 sixaxis_right_lifo_state = {};
559
532 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { 560 if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
533 controller.sixaxis_at_rest = true; 561 controller.sixaxis_at_rest = true;
534 for (std::size_t e = 0; e < motion_state.size(); ++e) { 562 for (std::size_t e = 0; e < motion_state.size(); ++e) {
@@ -537,69 +565,56 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
537 } 565 }
538 } 566 }
539 567
568 const auto set_motion_state = [&](SixAxisSensorState& state,
569 const Core::HID::ControllerMotion& hid_state) {
570 using namespace std::literals::chrono_literals;
571 static constexpr SixAxisSensorState default_motion_state = {
572 .delta_time = std::chrono::nanoseconds(5ms).count(),
573 .accel = {0, 0, -1.0f},
574 .orientation =
575 {
576 Common::Vec3f{1.0f, 0, 0},
577 Common::Vec3f{0, 1.0f, 0},
578 Common::Vec3f{0, 0, 1.0f},
579 },
580 .attribute = {1},
581 };
582 if (!controller.sixaxis_sensor_enabled) {
583 state = default_motion_state;
584 return;
585 }
586 if (!Settings::values.motion_enabled.GetValue()) {
587 state = default_motion_state;
588 return;
589 }
590 state.attribute.is_connected.Assign(1);
591 state.delta_time = std::chrono::nanoseconds(5ms).count();
592 state.accel = hid_state.accel;
593 state.gyro = hid_state.gyro;
594 state.rotation = hid_state.rotation;
595 state.orientation = hid_state.orientation;
596 };
597
540 switch (controller_type) { 598 switch (controller_type) {
541 case Core::HID::NpadStyleIndex::None: 599 case Core::HID::NpadStyleIndex::None:
542 UNREACHABLE(); 600 ASSERT(false);
543 break; 601 break;
544 case Core::HID::NpadStyleIndex::ProController: 602 case Core::HID::NpadStyleIndex::ProController:
545 sixaxis_fullkey_state.attribute.raw = 0; 603 case Core::HID::NpadStyleIndex::Pokeball:
546 if (controller.sixaxis_sensor_enabled) { 604 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
547 sixaxis_fullkey_state.attribute.is_connected.Assign(1);
548 sixaxis_fullkey_state.accel = motion_state[0].accel;
549 sixaxis_fullkey_state.gyro = motion_state[0].gyro;
550 sixaxis_fullkey_state.rotation = motion_state[0].rotation;
551 sixaxis_fullkey_state.orientation = motion_state[0].orientation;
552 }
553 break; 605 break;
554 case Core::HID::NpadStyleIndex::Handheld: 606 case Core::HID::NpadStyleIndex::Handheld:
555 sixaxis_handheld_state.attribute.raw = 0; 607 set_motion_state(sixaxis_handheld_state, motion_state[0]);
556 if (controller.sixaxis_sensor_enabled) {
557 sixaxis_handheld_state.attribute.is_connected.Assign(1);
558 sixaxis_handheld_state.accel = motion_state[0].accel;
559 sixaxis_handheld_state.gyro = motion_state[0].gyro;
560 sixaxis_handheld_state.rotation = motion_state[0].rotation;
561 sixaxis_handheld_state.orientation = motion_state[0].orientation;
562 }
563 break; 608 break;
564 case Core::HID::NpadStyleIndex::JoyconDual: 609 case Core::HID::NpadStyleIndex::JoyconDual:
565 sixaxis_dual_left_state.attribute.raw = 0; 610 set_motion_state(sixaxis_dual_left_state, motion_state[0]);
566 sixaxis_dual_right_state.attribute.raw = 0; 611 set_motion_state(sixaxis_dual_right_state, motion_state[1]);
567 if (controller.sixaxis_sensor_enabled) {
568 // Set motion for the left joycon
569 sixaxis_dual_left_state.attribute.is_connected.Assign(1);
570 sixaxis_dual_left_state.accel = motion_state[0].accel;
571 sixaxis_dual_left_state.gyro = motion_state[0].gyro;
572 sixaxis_dual_left_state.rotation = motion_state[0].rotation;
573 sixaxis_dual_left_state.orientation = motion_state[0].orientation;
574 }
575 if (controller.sixaxis_sensor_enabled) {
576 // Set motion for the right joycon
577 sixaxis_dual_right_state.attribute.is_connected.Assign(1);
578 sixaxis_dual_right_state.accel = motion_state[1].accel;
579 sixaxis_dual_right_state.gyro = motion_state[1].gyro;
580 sixaxis_dual_right_state.rotation = motion_state[1].rotation;
581 sixaxis_dual_right_state.orientation = motion_state[1].orientation;
582 }
583 break; 612 break;
584 case Core::HID::NpadStyleIndex::JoyconLeft: 613 case Core::HID::NpadStyleIndex::JoyconLeft:
585 sixaxis_left_lifo_state.attribute.raw = 0; 614 set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
586 if (controller.sixaxis_sensor_enabled) {
587 sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
588 sixaxis_left_lifo_state.accel = motion_state[0].accel;
589 sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
590 sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
591 sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
592 }
593 break; 615 break;
594 case Core::HID::NpadStyleIndex::JoyconRight: 616 case Core::HID::NpadStyleIndex::JoyconRight:
595 sixaxis_right_lifo_state.attribute.raw = 0; 617 set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
596 if (controller.sixaxis_sensor_enabled) {
597 sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
598 sixaxis_right_lifo_state.accel = motion_state[1].accel;
599 sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
600 sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
601 sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
602 }
603 break; 618 break;
604 default: 619 default:
605 break; 620 break;
@@ -676,6 +691,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
676} 691}
677 692
678void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 693void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
694 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
695 joy_hold_type != NpadJoyHoldType::Vertical) {
696 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
697 joy_hold_type);
698 return;
699 }
679 hold_type = joy_hold_type; 700 hold_type = joy_hold_type;
680} 701}
681 702
@@ -699,11 +720,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
699 return communication_mode; 720 return communication_mode;
700} 721}
701 722
702void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, 723ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
703 NpadJoyAssignmentMode assignment_mode) { 724 NpadJoyDeviceType npad_device_type,
725 NpadJoyAssignmentMode assignment_mode) {
704 if (!IsNpadIdValid(npad_id)) { 726 if (!IsNpadIdValid(npad_id)) {
705 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 727 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
706 return; 728 return InvalidNpadId;
707 } 729 }
708 730
709 auto& controller = GetControllerFromNpadIdType(npad_id); 731 auto& controller = GetControllerFromNpadIdType(npad_id);
@@ -712,7 +734,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
712 } 734 }
713 735
714 if (!controller.device->IsConnected()) { 736 if (!controller.device->IsConnected()) {
715 return; 737 return ResultSuccess;
716 } 738 }
717 739
718 if (assignment_mode == NpadJoyAssignmentMode::Dual) { 740 if (assignment_mode == NpadJoyAssignmentMode::Dual) {
@@ -721,34 +743,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
721 controller.is_dual_left_connected = true; 743 controller.is_dual_left_connected = true;
722 controller.is_dual_right_connected = false; 744 controller.is_dual_right_connected = false;
723 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 745 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
724 return; 746 return ResultSuccess;
725 } 747 }
726 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { 748 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
727 DisconnectNpad(npad_id); 749 DisconnectNpad(npad_id);
728 controller.is_dual_left_connected = false; 750 controller.is_dual_left_connected = false;
729 controller.is_dual_right_connected = true; 751 controller.is_dual_right_connected = true;
730 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 752 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
731 return; 753 return ResultSuccess;
732 } 754 }
733 return; 755 return ResultSuccess;
734 } 756 }
735 757
736 // This is for NpadJoyAssignmentMode::Single 758 // This is for NpadJoyAssignmentMode::Single
737 759
738 // Only JoyconDual get affected by this function 760 // Only JoyconDual get affected by this function
739 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { 761 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
740 return; 762 return ResultSuccess;
741 } 763 }
742 764
743 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { 765 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
744 DisconnectNpad(npad_id); 766 DisconnectNpad(npad_id);
745 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); 767 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
746 return; 768 return ResultSuccess;
747 } 769 }
748 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { 770 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
749 DisconnectNpad(npad_id); 771 DisconnectNpad(npad_id);
750 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); 772 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
751 return; 773 return ResultSuccess;
752 } 774 }
753 775
754 // We have two controllers connected to the same npad_id we need to split them 776 // We have two controllers connected to the same npad_id we need to split them
@@ -766,6 +788,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
766 controller_2.is_dual_right_connected = false; 788 controller_2.is_dual_right_connected = false;
767 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); 789 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
768 } 790 }
791 return ResultSuccess;
769} 792}
770 793
771bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 794bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -833,7 +856,7 @@ void Controller_NPad::VibrateController(
833 } 856 }
834 857
835 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) { 858 if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
836 UNREACHABLE_MSG("DeviceIndex should never be None!"); 859 ASSERT_MSG(false, "DeviceIndex should never be None!");
837 return; 860 return;
838 } 861 }
839 862
@@ -961,10 +984,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
961 InitNewlyAddedController(npad_id); 984 InitNewlyAddedController(npad_id);
962} 985}
963 986
964void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 987ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
965 if (!IsNpadIdValid(npad_id)) { 988 if (!IsNpadIdValid(npad_id)) {
966 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 989 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
967 return; 990 return InvalidNpadId;
968 } 991 }
969 992
970 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); 993 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
@@ -981,6 +1004,12 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
981 shared_memory->device_type.raw = 0; 1004 shared_memory->device_type.raw = 0;
982 shared_memory->system_properties.raw = 0; 1005 shared_memory->system_properties.raw = 0;
983 shared_memory->button_properties.raw = 0; 1006 shared_memory->button_properties.raw = 0;
1007 shared_memory->sixaxis_fullkey_properties.raw = 0;
1008 shared_memory->sixaxis_handheld_properties.raw = 0;
1009 shared_memory->sixaxis_dual_left_properties.raw = 0;
1010 shared_memory->sixaxis_dual_right_properties.raw = 0;
1011 shared_memory->sixaxis_left_properties.raw = 0;
1012 shared_memory->sixaxis_right_properties.raw = 0;
984 shared_memory->battery_level_dual = 0; 1013 shared_memory->battery_level_dual = 0;
985 shared_memory->battery_level_left = 0; 1014 shared_memory->battery_level_left = 0;
986 shared_memory->battery_level_right = 0; 1015 shared_memory->battery_level_right = 0;
@@ -1001,346 +1030,268 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1001 controller.device->Disconnect(); 1030 controller.device->Disconnect();
1002 SignalStyleSetChangedEvent(npad_id); 1031 SignalStyleSetChangedEvent(npad_id);
1003 WriteEmptyEntry(shared_memory); 1032 WriteEmptyEntry(shared_memory);
1033 return ResultSuccess;
1004} 1034}
1005 1035ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
1006ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1036 const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
1007 GyroscopeZeroDriftMode drift_mode) { 1037 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1008 if (!IsDeviceHandleValid(sixaxis_handle)) { 1038 if (is_valid.IsError()) {
1009 LOG_ERROR(Service_HID, "Invalid handle"); 1039 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1010 return NpadInvalidHandle; 1040 return is_valid;
1011 } 1041 }
1012 1042
1013 auto& controller = GetControllerFromHandle(sixaxis_handle); 1043 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1014 switch (sixaxis_handle.npad_type) { 1044 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1015 case Core::HID::NpadStyleIndex::ProController:
1016 controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
1017 break;
1018 case Core::HID::NpadStyleIndex::Handheld:
1019 controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
1020 break;
1021 case Core::HID::NpadStyleIndex::JoyconDual:
1022 case Core::HID::NpadStyleIndex::GameCube:
1023 case Core::HID::NpadStyleIndex::Pokeball:
1024 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1025 controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
1026 break;
1027 }
1028 controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
1029 break;
1030 case Core::HID::NpadStyleIndex::JoyconLeft:
1031 controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
1032 break;
1033 case Core::HID::NpadStyleIndex::JoyconRight:
1034 controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
1035 break;
1036 default:
1037 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1038 return NpadInvalidHandle;
1039 }
1040 1045
1041 return ResultSuccess; 1046 return ResultSuccess;
1042} 1047}
1043 1048
1044ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1049ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
1045 GyroscopeZeroDriftMode& drift_mode) const { 1050 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1046 if (!IsDeviceHandleValid(sixaxis_handle)) { 1051 GyroscopeZeroDriftMode& drift_mode) const {
1047 LOG_ERROR(Service_HID, "Invalid handle"); 1052 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1048 return NpadInvalidHandle; 1053 if (is_valid.IsError()) {
1054 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1055 return is_valid;
1049 } 1056 }
1050 1057
1051 auto& controller = GetControllerFromHandle(sixaxis_handle); 1058 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1052 switch (sixaxis_handle.npad_type) { 1059 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1053 case Core::HID::NpadStyleIndex::ProController:
1054 drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
1055 break;
1056 case Core::HID::NpadStyleIndex::Handheld:
1057 drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
1058 break;
1059 case Core::HID::NpadStyleIndex::JoyconDual:
1060 case Core::HID::NpadStyleIndex::GameCube:
1061 case Core::HID::NpadStyleIndex::Pokeball:
1062 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1063 drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
1064 break;
1065 }
1066 drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
1067 break;
1068 case Core::HID::NpadStyleIndex::JoyconLeft:
1069 drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
1070 break;
1071 case Core::HID::NpadStyleIndex::JoyconRight:
1072 drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
1073 break;
1074 default:
1075 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1076 return NpadInvalidHandle;
1077 }
1078 1060
1079 return ResultSuccess; 1061 return ResultSuccess;
1080} 1062}
1081 1063
1082ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, 1064ResultCode Controller_NPad::IsSixAxisSensorAtRest(
1083 bool& is_at_rest) const { 1065 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
1084 if (!IsDeviceHandleValid(sixaxis_handle)) { 1066 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1085 LOG_ERROR(Service_HID, "Invalid handle"); 1067 if (is_valid.IsError()) {
1086 return NpadInvalidHandle; 1068 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1069 return is_valid;
1087 } 1070 }
1071
1088 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1072 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1089 is_at_rest = controller.sixaxis_at_rest; 1073 is_at_rest = controller.sixaxis_at_rest;
1090 return ResultSuccess; 1074 return ResultSuccess;
1091} 1075}
1092 1076
1093ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( 1077ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1094 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const { 1078 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1095 if (!IsDeviceHandleValid(sixaxis_handle)) { 1079 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1096 LOG_ERROR(Service_HID, "Invalid handle"); 1080 if (is_valid.IsError()) {
1097 return NpadInvalidHandle; 1081 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1082 return is_valid;
1098 } 1083 }
1099 1084
1100 // We don't support joycon firmware updates 1085 const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
1101 is_firmware_available = false; 1086 is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
1102 return ResultSuccess; 1087 return ResultSuccess;
1103} 1088}
1104 1089
1105ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1090ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
1091 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1092 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1093 if (is_valid.IsError()) {
1094 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1095 return is_valid;
1096 }
1097
1098 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1099 sixaxis.unaltered_passtrough = is_enabled;
1100 return ResultSuccess;
1101}
1102
1103ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1104 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1105 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1106 if (is_valid.IsError()) {
1107 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1108 return is_valid;
1109 }
1110
1111 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1112 is_enabled = sixaxis.unaltered_passtrough;
1113 return ResultSuccess;
1114}
1115
1116ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1117 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1118 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1119 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1120 if (is_valid.IsError()) {
1121 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1122 return is_valid;
1123 }
1124
1125 // TODO: Request this data to the controller. On error return 0xd8ca
1126 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1127 calibration = sixaxis.calibration;
1128 return ResultSuccess;
1129}
1130
1131ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
1132 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1133 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1134 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1135 if (is_valid.IsError()) {
1136 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1137 return is_valid;
1138 }
1139
1140 // TODO: Request this data to the controller. On error return 0xd8ca
1141 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1142 ic_information = sixaxis.ic_information;
1143 return ResultSuccess;
1144}
1145
1146ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1147 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1148 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1149 if (is_valid.IsError()) {
1150 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1151 return is_valid;
1152 }
1153
1154 auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
1155 sixaxis_properties.is_newly_assigned.Assign(0);
1156
1157 return ResultSuccess;
1158}
1159
1160ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1106 bool sixaxis_status) { 1161 bool sixaxis_status) {
1107 if (!IsDeviceHandleValid(sixaxis_handle)) { 1162 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1108 LOG_ERROR(Service_HID, "Invalid handle"); 1163 if (is_valid.IsError()) {
1109 return NpadInvalidHandle; 1164 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1165 return is_valid;
1110 } 1166 }
1167
1111 auto& controller = GetControllerFromHandle(sixaxis_handle); 1168 auto& controller = GetControllerFromHandle(sixaxis_handle);
1112 controller.sixaxis_sensor_enabled = sixaxis_status; 1169 controller.sixaxis_sensor_enabled = sixaxis_status;
1113 return ResultSuccess; 1170 return ResultSuccess;
1114} 1171}
1115 1172
1116ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled( 1173ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
1117 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const { 1174 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
1118 if (!IsDeviceHandleValid(sixaxis_handle)) { 1175 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1119 LOG_ERROR(Service_HID, "Invalid handle"); 1176 if (is_valid.IsError()) {
1120 return NpadInvalidHandle; 1177 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1178 return is_valid;
1121 } 1179 }
1122 1180
1123 auto& controller = GetControllerFromHandle(sixaxis_handle); 1181 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1124 switch (sixaxis_handle.npad_type) { 1182 is_fusion_enabled = sixaxis.is_fusion_enabled;
1125 case Core::HID::NpadStyleIndex::ProController:
1126 is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
1127 break;
1128 case Core::HID::NpadStyleIndex::Handheld:
1129 is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
1130 break;
1131 case Core::HID::NpadStyleIndex::JoyconDual:
1132 case Core::HID::NpadStyleIndex::GameCube:
1133 case Core::HID::NpadStyleIndex::Pokeball:
1134 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1135 is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
1136 break;
1137 }
1138 is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
1139 break;
1140 case Core::HID::NpadStyleIndex::JoyconLeft:
1141 is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
1142 break;
1143 case Core::HID::NpadStyleIndex::JoyconRight:
1144 is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
1145 break;
1146 default:
1147 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1148 return NpadInvalidHandle;
1149 }
1150 1183
1151 return ResultSuccess; 1184 return ResultSuccess;
1152} 1185}
1153ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1186ResultCode Controller_NPad::SetSixAxisFusionEnabled(
1154 bool is_fusion_enabled) { 1187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1155 if (!IsDeviceHandleValid(sixaxis_handle)) { 1188 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1156 LOG_ERROR(Service_HID, "Invalid handle"); 1189 if (is_valid.IsError()) {
1157 return NpadInvalidHandle; 1190 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1191 return is_valid;
1158 } 1192 }
1159 1193
1160 auto& controller = GetControllerFromHandle(sixaxis_handle); 1194 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1161 switch (sixaxis_handle.npad_type) { 1195 sixaxis.is_fusion_enabled = is_fusion_enabled;
1162 case Core::HID::NpadStyleIndex::ProController:
1163 controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
1164 break;
1165 case Core::HID::NpadStyleIndex::Handheld:
1166 controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
1167 break;
1168 case Core::HID::NpadStyleIndex::JoyconDual:
1169 case Core::HID::NpadStyleIndex::GameCube:
1170 case Core::HID::NpadStyleIndex::Pokeball:
1171 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1172 controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
1173 break;
1174 }
1175 controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
1176 break;
1177 case Core::HID::NpadStyleIndex::JoyconLeft:
1178 controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
1179 break;
1180 case Core::HID::NpadStyleIndex::JoyconRight:
1181 controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
1182 break;
1183 default:
1184 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1185 return NpadInvalidHandle;
1186 }
1187 1196
1188 return ResultSuccess; 1197 return ResultSuccess;
1189} 1198}
1190 1199
1191ResultCode Controller_NPad::SetSixAxisFusionParameters( 1200ResultCode Controller_NPad::SetSixAxisFusionParameters(
1192 Core::HID::SixAxisSensorHandle sixaxis_handle, 1201 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1193 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { 1202 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1194 if (!IsDeviceHandleValid(sixaxis_handle)) { 1203 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1195 LOG_ERROR(Service_HID, "Invalid handle"); 1204 if (is_valid.IsError()) {
1196 return NpadInvalidHandle; 1205 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1206 return is_valid;
1197 } 1207 }
1208
1198 const auto param1 = sixaxis_fusion_parameters.parameter1; 1209 const auto param1 = sixaxis_fusion_parameters.parameter1;
1199 if (param1 < 0.0f || param1 > 1.0f) { 1210 if (param1 < 0.0f || param1 > 1.0f) {
1200 return InvalidSixAxisFusionRange; 1211 return InvalidSixAxisFusionRange;
1201 } 1212 }
1202 1213
1203 auto& controller = GetControllerFromHandle(sixaxis_handle); 1214 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1204 switch (sixaxis_handle.npad_type) { 1215 sixaxis.fusion = sixaxis_fusion_parameters;
1205 case Core::HID::NpadStyleIndex::ProController:
1206 controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
1207 break;
1208 case Core::HID::NpadStyleIndex::Handheld:
1209 controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
1210 break;
1211 case Core::HID::NpadStyleIndex::JoyconDual:
1212 case Core::HID::NpadStyleIndex::GameCube:
1213 case Core::HID::NpadStyleIndex::Pokeball:
1214 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1215 controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
1216 break;
1217 }
1218 controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
1219 break;
1220 case Core::HID::NpadStyleIndex::JoyconLeft:
1221 controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
1222 break;
1223 case Core::HID::NpadStyleIndex::JoyconRight:
1224 controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
1225 break;
1226 default:
1227 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1228 return NpadInvalidHandle;
1229 }
1230 1216
1231 return ResultSuccess; 1217 return ResultSuccess;
1232} 1218}
1233 1219
1234ResultCode Controller_NPad::GetSixAxisFusionParameters( 1220ResultCode Controller_NPad::GetSixAxisFusionParameters(
1235 Core::HID::SixAxisSensorHandle sixaxis_handle, 1221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1236 Core::HID::SixAxisSensorFusionParameters& parameters) const { 1222 Core::HID::SixAxisSensorFusionParameters& parameters) const {
1237 if (!IsDeviceHandleValid(sixaxis_handle)) { 1223 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1238 LOG_ERROR(Service_HID, "Invalid handle"); 1224 if (is_valid.IsError()) {
1239 return NpadInvalidHandle; 1225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1226 return is_valid;
1240 } 1227 }
1241 1228
1242 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1229 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1243 switch (sixaxis_handle.npad_type) { 1230 parameters = sixaxis.fusion;
1244 case Core::HID::NpadStyleIndex::ProController:
1245 parameters = controller.sixaxis_fullkey.fusion;
1246 break;
1247 case Core::HID::NpadStyleIndex::Handheld:
1248 parameters = controller.sixaxis_handheld.fusion;
1249 break;
1250 case Core::HID::NpadStyleIndex::JoyconDual:
1251 case Core::HID::NpadStyleIndex::GameCube:
1252 case Core::HID::NpadStyleIndex::Pokeball:
1253 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1254 parameters = controller.sixaxis_dual_left.fusion;
1255 break;
1256 }
1257 parameters = controller.sixaxis_dual_right.fusion;
1258 break;
1259 case Core::HID::NpadStyleIndex::JoyconLeft:
1260 parameters = controller.sixaxis_left.fusion;
1261 break;
1262 case Core::HID::NpadStyleIndex::JoyconRight:
1263 parameters = controller.sixaxis_right.fusion;
1264 break;
1265 default:
1266 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1267 return NpadInvalidHandle;
1268 }
1269 1231
1270 return ResultSuccess; 1232 return ResultSuccess;
1271} 1233}
1272 1234
1273void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1235ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1274 Core::HID::NpadIdType npad_id_2) { 1236 Core::HID::NpadIdType npad_id_2) {
1275 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1237 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1276 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1238 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1277 npad_id_2); 1239 npad_id_2);
1278 return; 1240 return InvalidNpadId;
1279 } 1241 }
1280 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); 1242 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
1281 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); 1243 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
1282 const auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); 1244 auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
1283 const auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); 1245 auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
1284 bool merge_controllers = false;
1285 1246
1286 // If the controllers at both npad indices form a pair of left and right joycons, merge them. 1247 // Simplify this code by converting dualjoycon with only a side connected to single joycons
1287 // Otherwise, do nothing. 1248 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
1249 if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
1250 controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
1251 }
1252 if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1253 controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
1254 }
1255 }
1256 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
1257 if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1258 controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
1259 }
1260 if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1261 controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
1262 }
1263 }
1264
1265 // Invalid merge errors
1266 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
1267 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
1268 return NpadIsDualJoycon;
1269 }
1288 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && 1270 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1271 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
1272 return NpadIsSameType;
1273 }
1274 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1289 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { 1275 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
1290 merge_controllers = true; 1276 return NpadIsSameType;
1291 } 1277 }
1292 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && 1278
1293 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) { 1279 // These exceptions are handled as if they where dual joycon
1294 merge_controllers = true; 1280 if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
1295 } 1281 controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
1296 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && 1282 return NpadIsDualJoycon;
1297 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight && 1283 }
1298 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) { 1284 if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
1299 merge_controllers = true; 1285 controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
1300 } 1286 return NpadIsDualJoycon;
1301 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1302 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1303 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1304 merge_controllers = true;
1305 }
1306 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1307 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1308 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1309 merge_controllers = true;
1310 }
1311 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1312 controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1313 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1314 merge_controllers = true;
1315 }
1316 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1317 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1318 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
1319 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1320 merge_controllers = true;
1321 }
1322 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1323 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1324 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
1325 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1326 merge_controllers = true;
1327 }
1328
1329 if (merge_controllers) {
1330 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1331 DisconnectNpad(npad_id_2);
1332 controller_1.is_dual_left_connected = true;
1333 controller_1.is_dual_right_connected = true;
1334 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1335 return;
1336 } 1287 }
1337 LOG_WARNING(Service_HID, 1288
1338 "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, " 1289 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1339 "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}", 1290 DisconnectNpad(npad_id_2);
1340 npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(), 1291 controller_1.is_dual_left_connected = true;
1341 controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected, 1292 controller_1.is_dual_right_connected = true;
1342 controller_1.is_dual_right_connected, controller_2.is_dual_left_connected, 1293 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1343 controller_2.is_dual_right_connected); 1294 return ResultSuccess;
1344} 1295}
1345 1296
1346void Controller_NPad::StartLRAssignmentMode() { 1297void Controller_NPad::StartLRAssignmentMode() {
@@ -1353,17 +1304,17 @@ void Controller_NPad::StopLRAssignmentMode() {
1353 is_in_lr_assignment_mode = false; 1304 is_in_lr_assignment_mode = false;
1354} 1305}
1355 1306
1356bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1307ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1357 Core::HID::NpadIdType npad_id_2) { 1308 Core::HID::NpadIdType npad_id_2) {
1358 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1309 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1359 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1310 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1360 npad_id_2); 1311 npad_id_2);
1361 return false; 1312 return InvalidNpadId;
1362 } 1313 }
1363 if (npad_id_1 == Core::HID::NpadIdType::Handheld || 1314 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1364 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || 1315 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1365 npad_id_2 == Core::HID::NpadIdType::Other) { 1316 npad_id_2 == Core::HID::NpadIdType::Other) {
1366 return true; 1317 return ResultSuccess;
1367 } 1318 }
1368 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; 1319 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1369 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; 1320 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
@@ -1373,46 +1324,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1373 const auto is_connected_2 = controller_2->IsConnected(); 1324 const auto is_connected_2 = controller_2->IsConnected();
1374 1325
1375 if (!IsControllerSupported(type_index_1) && is_connected_1) { 1326 if (!IsControllerSupported(type_index_1) && is_connected_1) {
1376 return false; 1327 return NpadNotConnected;
1377 } 1328 }
1378 if (!IsControllerSupported(type_index_2) && is_connected_2) { 1329 if (!IsControllerSupported(type_index_2) && is_connected_2) {
1379 return false; 1330 return NpadNotConnected;
1380 } 1331 }
1381 1332
1382 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); 1333 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
1383 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); 1334 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
1384 1335
1385 return true; 1336 return ResultSuccess;
1386} 1337}
1387 1338
1388Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) { 1339ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1340 Core::HID::LedPattern& pattern) const {
1389 if (!IsNpadIdValid(npad_id)) { 1341 if (!IsNpadIdValid(npad_id)) {
1390 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1342 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1391 return Core::HID::LedPattern{0, 0, 0, 0}; 1343 return InvalidNpadId;
1392 } 1344 }
1393 const auto& controller = GetControllerFromNpadIdType(npad_id).device; 1345 const auto& controller = GetControllerFromNpadIdType(npad_id).device;
1394 return controller->GetLedPattern(); 1346 pattern = controller->GetLedPattern();
1347 return ResultSuccess;
1395} 1348}
1396 1349
1397bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled( 1350ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
1398 Core::HID::NpadIdType npad_id) const { 1351 Core::HID::NpadIdType npad_id, bool& is_valid) const {
1399 if (!IsNpadIdValid(npad_id)) { 1352 if (!IsNpadIdValid(npad_id)) {
1400 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1353 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1401 // Return the default value 1354 return InvalidNpadId;
1402 return false;
1403 } 1355 }
1404 const auto& controller = GetControllerFromNpadIdType(npad_id); 1356 const auto& controller = GetControllerFromNpadIdType(npad_id);
1405 return controller.unintended_home_button_input_protection; 1357 is_valid = controller.unintended_home_button_input_protection;
1358 return ResultSuccess;
1406} 1359}
1407 1360
1408void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1361ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1409 Core::HID::NpadIdType npad_id) { 1362 bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
1410 if (!IsNpadIdValid(npad_id)) { 1363 if (!IsNpadIdValid(npad_id)) {
1411 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1364 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1412 return; 1365 return InvalidNpadId;
1413 } 1366 }
1414 auto& controller = GetControllerFromNpadIdType(npad_id); 1367 auto& controller = GetControllerFromNpadIdType(npad_id);
1415 controller.unintended_home_button_input_protection = is_protection_enabled; 1368 controller.unintended_home_button_input_protection = is_protection_enabled;
1369 return ResultSuccess;
1416} 1370}
1417 1371
1418void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1372void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1550,4 +1504,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
1550 return controller_data[npad_index]; 1504 return controller_data[npad_index];
1551} 1505}
1552 1506
1507Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1508 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1509 auto& controller = GetControllerFromHandle(sixaxis_handle);
1510 switch (sixaxis_handle.npad_type) {
1511 case Core::HID::NpadStyleIndex::ProController:
1512 case Core::HID::NpadStyleIndex::Pokeball:
1513 return controller.shared_memory->sixaxis_fullkey_properties;
1514 case Core::HID::NpadStyleIndex::Handheld:
1515 return controller.shared_memory->sixaxis_handheld_properties;
1516 case Core::HID::NpadStyleIndex::JoyconDual:
1517 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1518 return controller.shared_memory->sixaxis_dual_left_properties;
1519 }
1520 return controller.shared_memory->sixaxis_dual_right_properties;
1521 case Core::HID::NpadStyleIndex::JoyconLeft:
1522 return controller.shared_memory->sixaxis_left_properties;
1523 case Core::HID::NpadStyleIndex::JoyconRight:
1524 return controller.shared_memory->sixaxis_right_properties;
1525 default:
1526 return controller.shared_memory->sixaxis_fullkey_properties;
1527 }
1528}
1529
1530const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1531 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1532 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1533 switch (sixaxis_handle.npad_type) {
1534 case Core::HID::NpadStyleIndex::ProController:
1535 case Core::HID::NpadStyleIndex::Pokeball:
1536 return controller.shared_memory->sixaxis_fullkey_properties;
1537 case Core::HID::NpadStyleIndex::Handheld:
1538 return controller.shared_memory->sixaxis_handheld_properties;
1539 case Core::HID::NpadStyleIndex::JoyconDual:
1540 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1541 return controller.shared_memory->sixaxis_dual_left_properties;
1542 }
1543 return controller.shared_memory->sixaxis_dual_right_properties;
1544 case Core::HID::NpadStyleIndex::JoyconLeft:
1545 return controller.shared_memory->sixaxis_left_properties;
1546 case Core::HID::NpadStyleIndex::JoyconRight:
1547 return controller.shared_memory->sixaxis_right_properties;
1548 default:
1549 return controller.shared_memory->sixaxis_fullkey_properties;
1550 }
1551}
1552
1553Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
1554 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1555 auto& controller = GetControllerFromHandle(sixaxis_handle);
1556 switch (sixaxis_handle.npad_type) {
1557 case Core::HID::NpadStyleIndex::ProController:
1558 case Core::HID::NpadStyleIndex::Pokeball:
1559 return controller.sixaxis_fullkey;
1560 case Core::HID::NpadStyleIndex::Handheld:
1561 return controller.sixaxis_handheld;
1562 case Core::HID::NpadStyleIndex::JoyconDual:
1563 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1564 return controller.sixaxis_dual_left;
1565 }
1566 return controller.sixaxis_dual_right;
1567 case Core::HID::NpadStyleIndex::JoyconLeft:
1568 return controller.sixaxis_left;
1569 case Core::HID::NpadStyleIndex::JoyconRight:
1570 return controller.sixaxis_right;
1571 default:
1572 return controller.sixaxis_unknown;
1573 }
1574}
1575
1576const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
1577 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1578 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1579 switch (sixaxis_handle.npad_type) {
1580 case Core::HID::NpadStyleIndex::ProController:
1581 case Core::HID::NpadStyleIndex::Pokeball:
1582 return controller.sixaxis_fullkey;
1583 case Core::HID::NpadStyleIndex::Handheld:
1584 return controller.sixaxis_handheld;
1585 case Core::HID::NpadStyleIndex::JoyconDual:
1586 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1587 return controller.sixaxis_dual_left;
1588 }
1589 return controller.sixaxis_dual_right;
1590 case Core::HID::NpadStyleIndex::JoyconLeft:
1591 return controller.sixaxis_left;
1592 case Core::HID::NpadStyleIndex::JoyconRight:
1593 return controller.sixaxis_right;
1594 default:
1595 return controller.sixaxis_unknown;
1596 }
1597}
1598
1553} // namespace Service::HID 1599} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 0a96825a5..0b662b7f8 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -107,8 +107,8 @@ public:
107 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 107 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
108 NpadCommunicationMode GetNpadCommunicationMode() const; 108 NpadCommunicationMode GetNpadCommunicationMode() const;
109 109
110 void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, 110 ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
111 NpadJoyAssignmentMode assignment_mode); 111 NpadJoyAssignmentMode assignment_mode);
112 112
113 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, 113 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
114 const Core::HID::VibrationValue& vibration_value); 114 const Core::HID::VibrationValue& vibration_value);
@@ -141,50 +141,65 @@ public:
141 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, 141 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
142 bool connected); 142 bool connected);
143 143
144 void DisconnectNpad(Core::HID::NpadIdType npad_id); 144 ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
145 145
146 ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 146 ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
147 GyroscopeZeroDriftMode drift_mode); 147 GyroscopeZeroDriftMode drift_mode);
148 ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 148 ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
149 GyroscopeZeroDriftMode& drift_mode) const; 149 GyroscopeZeroDriftMode& drift_mode) const;
150 ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, 150 ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
151 bool& is_at_rest) const; 151 bool& is_at_rest) const;
152 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor( 152 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
153 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const; 153 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
154 ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 154 ResultCode EnableSixAxisSensorUnalteredPassthrough(
155 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
156 ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
158 ResultCode LoadSixAxisSensorCalibrationParameter(
159 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
160 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
161 ResultCode GetSixAxisSensorIcInformation(
162 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
163 Core::HID::SixAxisSensorIcInformation& ic_information) const;
164 ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
165 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
166 ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
155 bool sixaxis_status); 167 bool sixaxis_status);
156 ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 168 ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
157 bool& is_fusion_enabled) const; 169 bool& is_fusion_enabled) const;
158 ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 170 ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
159 bool is_fusion_enabled); 171 bool is_fusion_enabled);
160 ResultCode SetSixAxisFusionParameters( 172 ResultCode SetSixAxisFusionParameters(
161 Core::HID::SixAxisSensorHandle sixaxis_handle, 173 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
162 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); 174 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
163 ResultCode GetSixAxisFusionParameters( 175 ResultCode GetSixAxisFusionParameters(
164 Core::HID::SixAxisSensorHandle sixaxis_handle, 176 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
165 Core::HID::SixAxisSensorFusionParameters& parameters) const; 177 Core::HID::SixAxisSensorFusionParameters& parameters) const;
166 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); 178 ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
167 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; 179 ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
168 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 180 bool& is_enabled) const;
169 Core::HID::NpadIdType npad_id); 181 ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
182 Core::HID::NpadIdType npad_id);
170 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 183 void SetAnalogStickUseCenterClamp(bool use_center_clamp);
171 void ClearAllConnectedControllers(); 184 void ClearAllConnectedControllers();
172 void DisconnectAllConnectedControllers(); 185 void DisconnectAllConnectedControllers();
173 void ConnectAllDisconnectedControllers(); 186 void ConnectAllDisconnectedControllers();
174 void ClearAllControllers(); 187 void ClearAllControllers();
175 188
176 void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); 189 ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
190 Core::HID::NpadIdType npad_id_2);
177 void StartLRAssignmentMode(); 191 void StartLRAssignmentMode();
178 void StopLRAssignmentMode(); 192 void StopLRAssignmentMode();
179 bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); 193 ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
180 194
181 // Logical OR for all buttons presses on all controllers 195 // Logical OR for all buttons presses on all controllers
182 // Specifically for cheat engine and other features. 196 // Specifically for cheat engine and other features.
183 Core::HID::NpadButton GetAndResetPressState(); 197 Core::HID::NpadButton GetAndResetPressState();
184 198
185 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 199 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
186 static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
187 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); 200 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
201 static ResultCode VerifyValidSixAxisSensorHandle(
202 const Core::HID::SixAxisSensorHandle& device_handle);
188 203
189private: 204private:
190 static constexpr std::size_t NPAD_COUNT = 10; 205 static constexpr std::size_t NPAD_COUNT = 10;
@@ -451,9 +466,13 @@ private:
451 NpadLuciaType lucia_type{}; 466 NpadLuciaType lucia_type{};
452 NpadLagonType lagon_type{}; 467 NpadLagonType lagon_type{};
453 NpadLagerType lager_type{}; 468 NpadLagerType lager_type{};
454 // FW 13.x Investigate there is some sort of bitflag related to joycons 469 Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
455 INSERT_PADDING_BYTES(0x4); 470 Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
456 INSERT_PADDING_BYTES(0xc08); // Unknown 471 Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
472 Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
473 Core::HID::SixAxisSensorProperties sixaxis_left_properties;
474 Core::HID::SixAxisSensorProperties sixaxis_right_properties;
475 INSERT_PADDING_BYTES(0xc06); // Unknown
457 }; 476 };
458 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size"); 477 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
459 478
@@ -465,7 +484,10 @@ private:
465 484
466 struct SixaxisParameters { 485 struct SixaxisParameters {
467 bool is_fusion_enabled{true}; 486 bool is_fusion_enabled{true};
487 bool unaltered_passtrough{false};
468 Core::HID::SixAxisSensorFusionParameters fusion{}; 488 Core::HID::SixAxisSensorFusionParameters fusion{};
489 Core::HID::SixAxisSensorCalibrationParameter calibration{};
490 Core::HID::SixAxisSensorIcInformation ic_information{};
469 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 491 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
470 }; 492 };
471 493
@@ -491,6 +513,7 @@ private:
491 SixaxisParameters sixaxis_dual_right{}; 513 SixaxisParameters sixaxis_dual_right{};
492 SixaxisParameters sixaxis_left{}; 514 SixaxisParameters sixaxis_left{};
493 SixaxisParameters sixaxis_right{}; 515 SixaxisParameters sixaxis_right{};
516 SixaxisParameters sixaxis_unknown{};
494 517
495 // Current pad state 518 // Current pad state
496 NPadGenericState npad_pad_state{}; 519 NPadGenericState npad_pad_state{};
@@ -522,6 +545,14 @@ private:
522 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 545 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
523 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 546 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
524 547
548 Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
549 const Core::HID::SixAxisSensorHandle& device_handle);
550 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
551 const Core::HID::SixAxisSensorHandle& device_handle) const;
552 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
553 const SixaxisParameters& GetSixaxisState(
554 const Core::HID::SixAxisSensorHandle& device_handle) const;
555
525 std::atomic<u64> press_state{}; 556 std::atomic<u64> press_state{};
526 557
527 std::array<NpadControllerData, NPAD_COUNT> controller_data{}; 558 std::array<NpadControllerData, NPAD_COUNT> controller_data{};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 108ce5a41..1da8d3eb0 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -44,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
44 for (std::size_t id = 0; id < MAX_FINGERS; id++) { 44 for (std::size_t id = 0; id < MAX_FINGERS; id++) {
45 const auto& current_touch = touch_status[id]; 45 const auto& current_touch = touch_status[id];
46 auto& finger = fingers[id]; 46 auto& finger = fingers[id];
47 finger.position = current_touch.position;
48 finger.id = current_touch.id; 47 finger.id = current_touch.id;
49 48
50 if (finger.attribute.start_touch) { 49 if (finger.attribute.start_touch) {
@@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
61 if (!finger.pressed && current_touch.pressed) { 60 if (!finger.pressed && current_touch.pressed) {
62 finger.attribute.start_touch.Assign(1); 61 finger.attribute.start_touch.Assign(1);
63 finger.pressed = true; 62 finger.pressed = true;
63 finger.position = current_touch.position;
64 continue; 64 continue;
65 } 65 }
66 66
67 if (finger.pressed && !current_touch.pressed) { 67 if (finger.pressed && !current_touch.pressed) {
68 finger.attribute.raw = 0; 68 finger.attribute.raw = 0;
69 finger.attribute.end_touch.Assign(1); 69 finger.attribute.end_touch.Assign(1);
70 continue;
70 } 71 }
72
73 // Only update position if touch is not on a special frame
74 finger.position = current_touch.position;
71 } 75 }
72 76
73 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; 77 std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index b31834074..6c8ad04af 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -8,7 +8,11 @@
8namespace Service::HID { 8namespace Service::HID {
9 9
10constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100}; 10constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
11constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
11constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423}; 12constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
14constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
15constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
12constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710}; 16constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
13 17
14} // namespace Service::HID 18} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index eba44eda8..dc5d0366d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -37,8 +37,7 @@ namespace Service::HID {
37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew 37// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz) 38constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) 39constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
40// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed 40constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
41constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz)
42 41
43IAppletResource::IAppletResource(Core::System& system_, 42IAppletResource::IAppletResource(Core::System& system_,
44 KernelHelpers::ServiceContext& service_context_) 43 KernelHelpers::ServiceContext& service_context_)
@@ -258,12 +257,12 @@ Hid::Hid(Core::System& system_)
258 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, 257 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
259 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 258 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
260 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, 259 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
261 {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"}, 260 {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
262 {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"}, 261 {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
263 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, 262 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
264 {87, nullptr, "LoadSixAxisSensorCalibrationParameter"}, 263 {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
265 {88, nullptr, "GetSixAxisSensorIcInformation"}, 264 {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
266 {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, 265 {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
267 {91, &Hid::ActivateGesture, "ActivateGesture"}, 266 {91, &Hid::ActivateGesture, "ActivateGesture"},
268 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 267 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
269 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 268 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -695,11 +694,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
695 rb.Push(result1); 694 rb.Push(result1);
696 return; 695 return;
697 } 696 }
698 if (result2.IsError()) { 697 rb.Push(result2);
699 rb.Push(result2);
700 return;
701 }
702 rb.Push(ResultSuccess);
703} 698}
704 699
705void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 700void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -822,6 +817,144 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
822 rb.Push(is_firmware_available); 817 rb.Push(is_firmware_available);
823} 818}
824 819
820void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
821 IPC::RequestParser rp{ctx};
822 struct Parameters {
823 bool enabled;
824 Core::HID::SixAxisSensorHandle sixaxis_handle;
825 u64 applet_resource_user_id;
826 };
827 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
828
829 const auto parameters{rp.PopRaw<Parameters>()};
830
831 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
832 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
833 parameters.sixaxis_handle, parameters.enabled);
834
835 LOG_WARNING(Service_HID,
836 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
837 "applet_resource_user_id={}",
838 parameters.enabled, parameters.sixaxis_handle.npad_type,
839 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
840 parameters.applet_resource_user_id);
841
842 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(result);
844}
845
846void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
847 IPC::RequestParser rp{ctx};
848 struct Parameters {
849 Core::HID::SixAxisSensorHandle sixaxis_handle;
850 INSERT_PADDING_WORDS_NOINIT(1);
851 u64 applet_resource_user_id;
852 };
853 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
854
855 const auto parameters{rp.PopRaw<Parameters>()};
856
857 bool is_unaltered_sisxaxis_enabled{};
858 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
859 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
860 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
861
862 LOG_WARNING(
863 Service_HID,
864 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
865 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
866 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
867
868 IPC::ResponseBuilder rb{ctx, 3};
869 rb.Push(result);
870 rb.Push(is_unaltered_sisxaxis_enabled);
871}
872
873void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
874 IPC::RequestParser rp{ctx};
875 struct Parameters {
876 Core::HID::SixAxisSensorHandle sixaxis_handle;
877 INSERT_PADDING_WORDS_NOINIT(1);
878 u64 applet_resource_user_id;
879 };
880 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
881
882 const auto parameters{rp.PopRaw<Parameters>()};
883
884 Core::HID::SixAxisSensorCalibrationParameter calibration{};
885 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
886 const auto result =
887 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
888
889 LOG_WARNING(
890 Service_HID,
891 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
892 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
893 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
894
895 if (result.IsSuccess()) {
896 ctx.WriteBuffer(calibration);
897 }
898
899 IPC::ResponseBuilder rb{ctx, 2};
900 rb.Push(result);
901}
902
903void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
904 IPC::RequestParser rp{ctx};
905 struct Parameters {
906 Core::HID::SixAxisSensorHandle sixaxis_handle;
907 INSERT_PADDING_WORDS_NOINIT(1);
908 u64 applet_resource_user_id;
909 };
910 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
911
912 const auto parameters{rp.PopRaw<Parameters>()};
913
914 Core::HID::SixAxisSensorIcInformation ic_information{};
915 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
916 const auto result =
917 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
918
919 LOG_WARNING(
920 Service_HID,
921 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
922 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
923 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
924
925 if (result.IsSuccess()) {
926 ctx.WriteBuffer(ic_information);
927 }
928
929 IPC::ResponseBuilder rb{ctx, 2};
930 rb.Push(result);
931}
932
933void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
934 IPC::RequestParser rp{ctx};
935 struct Parameters {
936 Core::HID::SixAxisSensorHandle sixaxis_handle;
937 INSERT_PADDING_WORDS_NOINIT(1);
938 u64 applet_resource_user_id;
939 };
940 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
941
942 const auto parameters{rp.PopRaw<Parameters>()};
943
944 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
945 const auto result =
946 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
947
948 LOG_WARNING(
949 Service_HID,
950 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
951 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
952 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
953
954 IPC::ResponseBuilder rb{ctx, 2};
955 rb.Push(result);
956}
957
825void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 958void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
826 IPC::RequestParser rp{ctx}; 959 IPC::RequestParser rp{ctx};
827 struct Parameters { 960 struct Parameters {
@@ -949,27 +1082,29 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
949 1082
950 const auto parameters{rp.PopRaw<Parameters>()}; 1083 const auto parameters{rp.PopRaw<Parameters>()};
951 1084
952 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1085 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
953 .DisconnectNpad(parameters.npad_id); 1086 const auto result = controller.DisconnectNpad(parameters.npad_id);
954 1087
955 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1088 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
956 parameters.applet_resource_user_id); 1089 parameters.applet_resource_user_id);
957 1090
958 IPC::ResponseBuilder rb{ctx, 2}; 1091 IPC::ResponseBuilder rb{ctx, 2};
959 rb.Push(ResultSuccess); 1092 rb.Push(result);
960} 1093}
961 1094
962void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 1095void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
963 IPC::RequestParser rp{ctx}; 1096 IPC::RequestParser rp{ctx};
964 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; 1097 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
965 1098
1099 Core::HID::LedPattern pattern{0, 0, 0, 0};
1100 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1101 const auto result = controller.GetLedPattern(npad_id, pattern);
1102
966 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 1103 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
967 1104
968 IPC::ResponseBuilder rb{ctx, 4}; 1105 IPC::ResponseBuilder rb{ctx, 4};
969 rb.Push(ResultSuccess); 1106 rb.Push(result);
970 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 1107 rb.Push(pattern.raw);
971 .GetLedPattern(npad_id)
972 .raw);
973} 1108}
974 1109
975void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { 1110void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
@@ -1029,15 +1164,16 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
1029 1164
1030 const auto parameters{rp.PopRaw<Parameters>()}; 1165 const auto parameters{rp.PopRaw<Parameters>()};
1031 1166
1032 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1167 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1033 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, 1168 const auto result =
1034 Controller_NPad::NpadJoyAssignmentMode::Single); 1169 controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
1170 Controller_NPad::NpadJoyAssignmentMode::Single);
1035 1171
1036 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1172 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1037 parameters.applet_resource_user_id); 1173 parameters.applet_resource_user_id);
1038 1174
1039 IPC::ResponseBuilder rb{ctx, 2}; 1175 IPC::ResponseBuilder rb{ctx, 2};
1040 rb.Push(ResultSuccess); 1176 rb.Push(result);
1041} 1177}
1042 1178
1043void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 1179void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
@@ -1052,16 +1188,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
1052 1188
1053 const auto parameters{rp.PopRaw<Parameters>()}; 1189 const auto parameters{rp.PopRaw<Parameters>()};
1054 1190
1055 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1191 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1056 .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, 1192 const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
1057 Controller_NPad::NpadJoyAssignmentMode::Single); 1193 Controller_NPad::NpadJoyAssignmentMode::Single);
1058 1194
1059 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1195 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1060 parameters.npad_id, parameters.applet_resource_user_id, 1196 parameters.npad_id, parameters.applet_resource_user_id,
1061 parameters.npad_joy_device_type); 1197 parameters.npad_joy_device_type);
1062 1198
1063 IPC::ResponseBuilder rb{ctx, 2}; 1199 IPC::ResponseBuilder rb{ctx, 2};
1064 rb.Push(ResultSuccess); 1200 rb.Push(result);
1065} 1201}
1066 1202
1067void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1203void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
@@ -1075,14 +1211,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
1075 1211
1076 const auto parameters{rp.PopRaw<Parameters>()}; 1212 const auto parameters{rp.PopRaw<Parameters>()};
1077 1213
1078 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1214 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1079 .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); 1215 const auto result = controller.SetNpadMode(parameters.npad_id, {},
1216 Controller_NPad::NpadJoyAssignmentMode::Dual);
1080 1217
1081 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1218 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1082 parameters.applet_resource_user_id); 1219 parameters.applet_resource_user_id);
1083 1220
1084 IPC::ResponseBuilder rb{ctx, 2}; 1221 IPC::ResponseBuilder rb{ctx, 2};
1085 rb.Push(ResultSuccess); 1222 rb.Push(result);
1086} 1223}
1087 1224
1088void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1225void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1091,14 +1228,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
1091 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1228 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1092 const auto applet_resource_user_id{rp.Pop<u64>()}; 1229 const auto applet_resource_user_id{rp.Pop<u64>()};
1093 1230
1094 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1231 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1095 .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); 1232 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1096 1233
1097 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1234 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1098 npad_id_1, npad_id_2, applet_resource_user_id); 1235 npad_id_1, npad_id_2, applet_resource_user_id);
1099 1236
1100 IPC::ResponseBuilder rb{ctx, 2}; 1237 IPC::ResponseBuilder rb{ctx, 2};
1101 rb.Push(ResultSuccess); 1238 rb.Push(result);
1102} 1239}
1103 1240
1104void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { 1241void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
@@ -1158,19 +1295,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1158 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1295 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1159 const auto applet_resource_user_id{rp.Pop<u64>()}; 1296 const auto applet_resource_user_id{rp.Pop<u64>()};
1160 1297
1161 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) 1298 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1162 .SwapNpadAssignment(npad_id_1, npad_id_2); 1299 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
1163 1300
1164 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1301 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1165 npad_id_1, npad_id_2, applet_resource_user_id); 1302 npad_id_1, npad_id_2, applet_resource_user_id);
1166 1303
1167 IPC::ResponseBuilder rb{ctx, 2}; 1304 IPC::ResponseBuilder rb{ctx, 2};
1168 if (res) { 1305 rb.Push(result);
1169 rb.Push(ResultSuccess);
1170 } else {
1171 LOG_ERROR(Service_HID, "Npads are not connected!");
1172 rb.Push(NpadNotConnected);
1173 }
1174} 1306}
1175 1307
1176void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 1308void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
@@ -1184,13 +1316,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
1184 1316
1185 const auto parameters{rp.PopRaw<Parameters>()}; 1317 const auto parameters{rp.PopRaw<Parameters>()};
1186 1318
1319 bool is_enabled = false;
1320 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1321 const auto result =
1322 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1323
1187 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1324 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1188 parameters.npad_id, parameters.applet_resource_user_id); 1325 parameters.npad_id, parameters.applet_resource_user_id);
1189 1326
1190 IPC::ResponseBuilder rb{ctx, 3}; 1327 IPC::ResponseBuilder rb{ctx, 3};
1191 rb.Push(ResultSuccess); 1328 rb.Push(result);
1192 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 1329 rb.Push(is_enabled);
1193 .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
1194} 1330}
1195 1331
1196void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { 1332void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
@@ -1205,9 +1341,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1205 1341
1206 const auto parameters{rp.PopRaw<Parameters>()}; 1342 const auto parameters{rp.PopRaw<Parameters>()};
1207 1343
1208 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1344 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1209 .SetUnintendedHomeButtonInputProtectionEnabled( 1345 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
1210 parameters.unintended_home_button_input_protection, parameters.npad_id); 1346 parameters.unintended_home_button_input_protection, parameters.npad_id);
1211 1347
1212 LOG_WARNING(Service_HID, 1348 LOG_WARNING(Service_HID,
1213 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," 1349 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
@@ -1216,7 +1352,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1216 parameters.applet_resource_user_id); 1352 parameters.applet_resource_user_id);
1217 1353
1218 IPC::ResponseBuilder rb{ctx, 2}; 1354 IPC::ResponseBuilder rb{ctx, 2};
1219 rb.Push(ResultSuccess); 1355 rb.Push(result);
1220} 1356}
1221 1357
1222void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { 1358void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
@@ -1305,7 +1441,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
1305 break; 1441 break;
1306 case Core::HID::DeviceIndex::None: 1442 case Core::HID::DeviceIndex::None:
1307 default: 1443 default:
1308 UNREACHABLE_MSG("DeviceIndex should never be None!"); 1444 ASSERT_MSG(false, "DeviceIndex should never be None!");
1309 vibration_device_info.position = Core::HID::VibrationDevicePosition::None; 1445 vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
1310 break; 1446 break;
1311 } 1447 }
@@ -1378,6 +1514,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
1378 IPC::RequestParser rp{ctx}; 1514 IPC::RequestParser rp{ctx};
1379 const auto can_vibrate{rp.Pop<bool>()}; 1515 const auto can_vibrate{rp.Pop<bool>()};
1380 1516
1517 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1518 // by converting it to a bool
1381 Settings::values.vibration_enabled.SetValue(can_vibrate); 1519 Settings::values.vibration_enabled.SetValue(can_vibrate);
1382 1520
1383 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 1521 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -1389,9 +1527,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
1389void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { 1527void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
1390 LOG_DEBUG(Service_HID, "called"); 1528 LOG_DEBUG(Service_HID, "called");
1391 1529
1530 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1531 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1532
1392 IPC::ResponseBuilder rb{ctx, 3}; 1533 IPC::ResponseBuilder rb{ctx, 3};
1393 rb.Push(ResultSuccess); 1534 rb.Push(ResultSuccess);
1394 rb.Push(Settings::values.vibration_enabled.GetValue()); 1535 rb.Push(is_enabled);
1395} 1536}
1396 1537
1397void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { 1538void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1be04c22b..ac4333022 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -113,6 +113,11 @@ private:
113 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 113 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
114 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); 114 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
115 void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx); 115 void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
116 void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
117 void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
118 void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
119 void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
120 void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
116 void ActivateGesture(Kernel::HLERequestContext& ctx); 121 void ActivateGesture(Kernel::HLERequestContext& ctx);
117 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 122 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
118 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 123 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 9e32f3e60..d2a91d913 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -5,7 +5,9 @@
5#include "core/core_timing.h" 5#include "core/core_timing.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_shared_memory.h" 7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
10#include "core/hle/service/hid/errors.h"
9#include "core/hle/service/hid/irs.h" 11#include "core/hle/service/hid/irs.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
@@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
38} 40}
39 41
40void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { 42void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
41 LOG_WARNING(Service_IRS, "(STUBBED) called"); 43 IPC::RequestParser rp{ctx};
44 const auto applet_resource_user_id{rp.Pop<u64>()};
45
46 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
47 applet_resource_user_id);
42 48
43 IPC::ResponseBuilder rb{ctx, 2}; 49 IPC::ResponseBuilder rb{ctx, 2};
44 rb.Push(ResultSuccess); 50 rb.Push(ResultSuccess);
45} 51}
46 52
47void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { 53void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
48 LOG_WARNING(Service_IRS, "(STUBBED) called"); 54 IPC::RequestParser rp{ctx};
55 const auto applet_resource_user_id{rp.Pop<u64>()};
56
57 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
58 applet_resource_user_id);
49 59
50 IPC::ResponseBuilder rb{ctx, 2}; 60 IPC::ResponseBuilder rb{ctx, 2};
51 rb.Push(ResultSuccess); 61 rb.Push(ResultSuccess);
52} 62}
53 63
54void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { 64void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
55 LOG_DEBUG(Service_IRS, "called"); 65 IPC::RequestParser rp{ctx};
66 const auto applet_resource_user_id{rp.Pop<u64>()};
67
68 LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
56 69
57 IPC::ResponseBuilder rb{ctx, 2, 1}; 70 IPC::ResponseBuilder rb{ctx, 2, 1};
58 rb.Push(ResultSuccess); 71 rb.Push(ResultSuccess);
@@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
60} 73}
61 74
62void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { 75void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_IRS, "(STUBBED) called"); 76 IPC::RequestParser rp{ctx};
77 struct Parameters {
78 IrCameraHandle camera_handle;
79 INSERT_PADDING_WORDS_NOINIT(1);
80 u64 applet_resource_user_id;
81 };
82 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
83
84 const auto parameters{rp.PopRaw<Parameters>()};
85
86 LOG_WARNING(Service_IRS,
87 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
88 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
89 parameters.applet_resource_user_id);
64 90
65 IPC::ResponseBuilder rb{ctx, 2}; 91 IPC::ResponseBuilder rb{ctx, 2};
66 rb.Push(ResultSuccess); 92 rb.Push(ResultSuccess);
67} 93}
68 94
69void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { 95void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_IRS, "(STUBBED) called"); 96 IPC::RequestParser rp{ctx};
97 struct Parameters {
98 IrCameraHandle camera_handle;
99 INSERT_PADDING_WORDS_NOINIT(1);
100 u64 applet_resource_user_id;
101 PackedMomentProcessorConfig processor_config;
102 };
103 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
104
105 const auto parameters{rp.PopRaw<Parameters>()};
106
107 LOG_WARNING(Service_IRS,
108 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
109 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
110 parameters.applet_resource_user_id);
71 111
72 IPC::ResponseBuilder rb{ctx, 2}; 112 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(ResultSuccess); 113 rb.Push(ResultSuccess);
74} 114}
75 115
76void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { 116void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
77 LOG_WARNING(Service_IRS, "(STUBBED) called"); 117 IPC::RequestParser rp{ctx};
118 struct Parameters {
119 IrCameraHandle camera_handle;
120 INSERT_PADDING_WORDS_NOINIT(1);
121 u64 applet_resource_user_id;
122 PackedClusteringProcessorConfig processor_config;
123 };
124 static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
125
126 const auto parameters{rp.PopRaw<Parameters>()};
127
128 LOG_WARNING(Service_IRS,
129 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
130 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
131 parameters.applet_resource_user_id);
78 132
79 IPC::ResponseBuilder rb{ctx, 2}; 133 IPC::ResponseBuilder rb{ctx, 2};
80 rb.Push(ResultSuccess); 134 rb.Push(ResultSuccess);
81} 135}
82 136
83void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { 137void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
84 LOG_WARNING(Service_IRS, "(STUBBED) called"); 138 IPC::RequestParser rp{ctx};
139 struct Parameters {
140 IrCameraHandle camera_handle;
141 INSERT_PADDING_WORDS_NOINIT(1);
142 u64 applet_resource_user_id;
143 PackedImageTransferProcessorConfig processor_config;
144 u32 transfer_memory_size;
145 };
146 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
147
148 const auto parameters{rp.PopRaw<Parameters>()};
149 const auto t_mem_handle{ctx.GetCopyHandle(0)};
150
151 auto t_mem =
152 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
153
154 LOG_WARNING(Service_IRS,
155 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
156 "applet_resource_user_id={}",
157 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
158 parameters.transfer_memory_size, parameters.applet_resource_user_id);
85 159
86 IPC::ResponseBuilder rb{ctx, 2}; 160 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(ResultSuccess); 161 rb.Push(ResultSuccess);
88} 162}
89 163
90void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { 164void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
91 LOG_WARNING(Service_IRS, "(STUBBED) called"); 165 IPC::RequestParser rp{ctx};
166 struct Parameters {
167 IrCameraHandle camera_handle;
168 INSERT_PADDING_WORDS_NOINIT(1);
169 u64 applet_resource_user_id;
170 };
171 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
172
173 const auto parameters{rp.PopRaw<Parameters>()};
174
175 LOG_WARNING(Service_IRS,
176 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
177 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
178 parameters.applet_resource_user_id);
92 179
93 IPC::ResponseBuilder rb{ctx, 5}; 180 IPC::ResponseBuilder rb{ctx, 5};
94 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
@@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
97} 184}
98 185
99void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { 186void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
100 LOG_WARNING(Service_IRS, "(STUBBED) called"); 187 IPC::RequestParser rp{ctx};
188 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
189 const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
190 const auto applet_resource_user_id{rp.Pop<u64>()};
191
192 LOG_WARNING(Service_IRS,
193 "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
194 "applet_resource_user_id={}",
195 camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
196 processor_config.required_mcu_version.major,
197 processor_config.required_mcu_version.minor, applet_resource_user_id);
101 198
102 IPC::ResponseBuilder rb{ctx, 2}; 199 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultSuccess); 200 rb.Push(ResultSuccess);
104} 201}
105 202
106void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { 203void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
107 LOG_WARNING(Service_IRS, "(STUBBED) called"); 204 IPC::RequestParser rp{ctx};
205 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
206
207 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
208 npad_id != Core::HID::NpadIdType::Handheld) {
209 IPC::ResponseBuilder rb{ctx, 2};
210 rb.Push(InvalidNpadId);
211 return;
212 }
213
214 IrCameraHandle camera_handle{
215 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
216 .npad_type = Core::HID::NpadStyleIndex::None,
217 };
218
219 LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
220 npad_id, camera_handle.npad_id, camera_handle.npad_type);
108 221
109 IPC::ResponseBuilder rb{ctx, 3}; 222 IPC::ResponseBuilder rb{ctx, 3};
110 rb.Push(ResultSuccess); 223 rb.Push(ResultSuccess);
111 rb.PushRaw<u32>(device_handle); 224 rb.PushRaw(camera_handle);
112} 225}
113 226
114void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { 227void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
115 LOG_WARNING(Service_IRS, "(STUBBED) called"); 228 IPC::RequestParser rp{ctx};
229 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
230 const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
231 const auto applet_resource_user_id{rp.Pop<u64>()};
232
233 LOG_WARNING(
234 Service_IRS,
235 "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
236 camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
237 processor_config.required_mcu_version.minor, applet_resource_user_id);
116 238
117 IPC::ResponseBuilder rb{ctx, 2}; 239 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(ResultSuccess); 240 rb.Push(ResultSuccess);
119} 241}
120 242
121void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { 243void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
122 LOG_WARNING(Service_IRS, "(STUBBED) called"); 244 IPC::RequestParser rp{ctx};
245 struct Parameters {
246 IrCameraHandle camera_handle;
247 INSERT_PADDING_WORDS_NOINIT(1);
248 u64 applet_resource_user_id;
249 };
250 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
251
252 const auto parameters{rp.PopRaw<Parameters>()};
253
254 LOG_WARNING(Service_IRS,
255 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
256 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
257 parameters.applet_resource_user_id);
123 258
124 IPC::ResponseBuilder rb{ctx, 2}; 259 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(ResultSuccess); 260 rb.Push(ResultSuccess);
126} 261}
127 262
128void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { 263void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
129 LOG_WARNING(Service_IRS, "(STUBBED) called"); 264 IPC::RequestParser rp{ctx};
265 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
266 const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
267 const auto applet_resource_user_id{rp.Pop<u64>()};
268
269 LOG_WARNING(
270 Service_IRS,
271 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
272 camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
273 mcu_version.minor);
130 274
131 IPC::ResponseBuilder rb{ctx, 2}; 275 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(ResultSuccess); 276 rb.Push(ResultSuccess);
133} 277}
134 278
135void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { 279void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
136 LOG_WARNING(Service_IRS, "(STUBBED) called"); 280 IPC::RequestParser rp{ctx};
281 struct Parameters {
282 IrCameraHandle camera_handle;
283 PackedFunctionLevel function_level;
284 u64 applet_resource_user_id;
285 };
286 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
287
288 const auto parameters{rp.PopRaw<Parameters>()};
289
290 LOG_WARNING(Service_IRS,
291 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
292 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
293 parameters.applet_resource_user_id);
137 294
138 IPC::ResponseBuilder rb{ctx, 2}; 295 IPC::ResponseBuilder rb{ctx, 2};
139 rb.Push(ResultSuccess); 296 rb.Push(ResultSuccess);
140} 297}
141 298
142void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { 299void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
143 LOG_WARNING(Service_IRS, "(STUBBED) called"); 300 IPC::RequestParser rp{ctx};
301 struct Parameters {
302 IrCameraHandle camera_handle;
303 INSERT_PADDING_WORDS_NOINIT(1);
304 u64 applet_resource_user_id;
305 PackedImageTransferProcessorExConfig processor_config;
306 u64 transfer_memory_size;
307 };
308 static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
309
310 const auto parameters{rp.PopRaw<Parameters>()};
311 const auto t_mem_handle{ctx.GetCopyHandle(0)};
312
313 auto t_mem =
314 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
315
316 LOG_WARNING(Service_IRS,
317 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
318 "applet_resource_user_id={}",
319 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
320 parameters.transfer_memory_size, parameters.applet_resource_user_id);
144 321
145 IPC::ResponseBuilder rb{ctx, 2}; 322 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultSuccess); 323 rb.Push(ResultSuccess);
147} 324}
148 325
149void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { 326void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
150 LOG_WARNING(Service_IRS, "(STUBBED) called"); 327 IPC::RequestParser rp{ctx};
328 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
329 const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
330 const auto applet_resource_user_id{rp.Pop<u64>()};
331
332 LOG_WARNING(Service_IRS,
333 "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
334 "applet_resource_user_id={}",
335 camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
336 processor_config.required_mcu_version.major,
337 processor_config.required_mcu_version.minor, applet_resource_user_id);
151 338
152 IPC::ResponseBuilder rb{ctx, 2}; 339 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(ResultSuccess); 340 rb.Push(ResultSuccess);
154} 341}
155 342
156void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { 343void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
157 LOG_WARNING(Service_IRS, "(STUBBED) called"); 344 IPC::RequestParser rp{ctx};
345 struct Parameters {
346 IrCameraHandle camera_handle;
347 INSERT_PADDING_WORDS_NOINIT(1);
348 u64 applet_resource_user_id;
349 };
350 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
351
352 const auto parameters{rp.PopRaw<Parameters>()};
353
354 LOG_WARNING(Service_IRS,
355 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
356 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
357 parameters.applet_resource_user_id);
158 358
159 IPC::ResponseBuilder rb{ctx, 2}; 359 IPC::ResponseBuilder rb{ctx, 2};
160 rb.Push(ResultSuccess); 360 rb.Push(ResultSuccess);
161} 361}
162 362
163void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { 363void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
164 LOG_WARNING(Service_IRS, "(STUBBED) called"); 364 IPC::RequestParser rp{ctx};
365 struct Parameters {
366 PackedFunctionLevel function_level;
367 INSERT_PADDING_WORDS_NOINIT(1);
368 u64 applet_resource_user_id;
369 };
370 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
371
372 const auto parameters{rp.PopRaw<Parameters>()};
373
374 LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
375 parameters.function_level.function_level, parameters.applet_resource_user_id);
165 376
166 IPC::ResponseBuilder rb{ctx, 2}; 377 IPC::ResponseBuilder rb{ctx, 2};
167 rb.Push(ResultSuccess); 378 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index efb29d3fd..361dc2213 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hid/hid_types.h"
6#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
7 8
8namespace Core { 9namespace Core {
@@ -17,6 +18,235 @@ public:
17 ~IRS() override; 18 ~IRS() override;
18 19
19private: 20private:
21 // This is nn::irsensor::IrCameraStatus
22 enum IrCameraStatus : u32 {
23 Available,
24 Unsupported,
25 Unconnected,
26 };
27
28 // This is nn::irsensor::IrCameraInternalStatus
29 enum IrCameraInternalStatus : u32 {
30 Stopped,
31 FirmwareUpdateNeeded,
32 Unkown2,
33 Unkown3,
34 Unkown4,
35 FirmwareVersionRequested,
36 FirmwareVersionIsInvalid,
37 Ready,
38 Setting,
39 };
40
41 // This is nn::irsensor::detail::StatusManager::IrSensorMode
42 enum IrSensorMode : u64 {
43 None,
44 MomentProcessor,
45 ClusteringProcessor,
46 ImageTransferProcessor,
47 PointingProcessorMarker,
48 TeraPluginProcessor,
49 IrLedProcessor,
50 };
51
52 // This is nn::irsensor::ImageProcessorStatus
53 enum ImageProcessorStatus : u8 {
54 stopped,
55 running,
56 };
57
58 // This is nn::irsensor::ImageTransferProcessorFormat
59 enum ImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65 };
66
67 // This is nn::irsensor::AdaptiveClusteringMode
68 enum AdaptiveClusteringMode : u8 {
69 StaticFov,
70 DynamicFov,
71 };
72
73 // This is nn::irsensor::AdaptiveClusteringTargetDistance
74 enum AdaptiveClusteringTargetDistance : u8 {
75 Near,
76 Middle,
77 Far,
78 };
79
80 // This is nn::irsensor::IrsHandAnalysisMode
81 enum IrsHandAnalysisMode : u8 {
82 Silhouette,
83 Image,
84 SilhoueteAndImage,
85 SilhuetteOnly,
86 };
87
88 // This is nn::irsensor::IrSensorFunctionLevel
89 enum IrSensorFunctionLevel : u8 {
90 unknown0,
91 unknown1,
92 unknown2,
93 unknown3,
94 unknown4,
95 };
96
97 // This is nn::irsensor::IrCameraHandle
98 struct IrCameraHandle {
99 u8 npad_id{};
100 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
101 INSERT_PADDING_BYTES(2);
102 };
103 static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
104
105 struct IrsRect {
106 s16 x;
107 s16 y;
108 s16 width;
109 s16 height;
110 };
111
112 // This is nn::irsensor::PackedMcuVersion
113 struct PackedMcuVersion {
114 u16 major;
115 u16 minor;
116 };
117 static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
118
119 // This is nn::irsensor::MomentProcessorConfig
120 struct MomentProcessorConfig {
121 u64 exposire_time;
122 u8 light_target;
123 u8 gain;
124 u8 is_negative_used;
125 INSERT_PADDING_BYTES(7);
126 IrsRect window_of_interest;
127 u8 preprocess;
128 u8 preprocess_intensity_threshold;
129 INSERT_PADDING_BYTES(5);
130 };
131 static_assert(sizeof(MomentProcessorConfig) == 0x28,
132 "MomentProcessorConfig is an invalid size");
133
134 // This is nn::irsensor::PackedMomentProcessorConfig
135 struct PackedMomentProcessorConfig {
136 u64 exposire_time;
137 u8 light_target;
138 u8 gain;
139 u8 is_negative_used;
140 INSERT_PADDING_BYTES(5);
141 IrsRect window_of_interest;
142 PackedMcuVersion required_mcu_version;
143 u8 preprocess;
144 u8 preprocess_intensity_threshold;
145 INSERT_PADDING_BYTES(2);
146 };
147 static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
148 "PackedMomentProcessorConfig is an invalid size");
149
150 // This is nn::irsensor::ClusteringProcessorConfig
151 struct ClusteringProcessorConfig {
152 u64 exposire_time;
153 u32 light_target;
154 u32 gain;
155 u8 is_negative_used;
156 INSERT_PADDING_BYTES(7);
157 IrsRect window_of_interest;
158 u32 pixel_count_min;
159 u32 pixel_count_max;
160 u32 object_intensity_min;
161 u8 is_external_light_filter_enabled;
162 INSERT_PADDING_BYTES(3);
163 };
164 static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
165 "ClusteringProcessorConfig is an invalid size");
166
167 // This is nn::irsensor::PackedClusteringProcessorConfig
168 struct PackedClusteringProcessorConfig {
169 u64 exposire_time;
170 u8 light_target;
171 u8 gain;
172 u8 is_negative_used;
173 INSERT_PADDING_BYTES(5);
174 IrsRect window_of_interest;
175 PackedMcuVersion required_mcu_version;
176 u32 pixel_count_min;
177 u32 pixel_count_max;
178 u32 object_intensity_min;
179 u8 is_external_light_filter_enabled;
180 INSERT_PADDING_BYTES(2);
181 };
182 static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
183 "PackedClusteringProcessorConfig is an invalid size");
184
185 // This is nn::irsensor::PackedImageTransferProcessorConfig
186 struct PackedImageTransferProcessorConfig {
187 u64 exposire_time;
188 u8 light_target;
189 u8 gain;
190 u8 is_negative_used;
191 INSERT_PADDING_BYTES(5);
192 PackedMcuVersion required_mcu_version;
193 u8 format;
194 INSERT_PADDING_BYTES(3);
195 };
196 static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
197 "PackedImageTransferProcessorConfig is an invalid size");
198
199 // This is nn::irsensor::PackedTeraPluginProcessorConfig
200 struct PackedTeraPluginProcessorConfig {
201 PackedMcuVersion required_mcu_version;
202 u8 mode;
203 INSERT_PADDING_BYTES(3);
204 };
205 static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
206 "PackedTeraPluginProcessorConfig is an invalid size");
207
208 // This is nn::irsensor::PackedPointingProcessorConfig
209 struct PackedPointingProcessorConfig {
210 IrsRect window_of_interest;
211 PackedMcuVersion required_mcu_version;
212 };
213 static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
214 "PackedPointingProcessorConfig is an invalid size");
215
216 // This is nn::irsensor::PackedFunctionLevel
217 struct PackedFunctionLevel {
218 IrSensorFunctionLevel function_level;
219 INSERT_PADDING_BYTES(3);
220 };
221 static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
222
223 // This is nn::irsensor::PackedImageTransferProcessorExConfig
224 struct PackedImageTransferProcessorExConfig {
225 u64 exposire_time;
226 u8 light_target;
227 u8 gain;
228 u8 is_negative_used;
229 INSERT_PADDING_BYTES(5);
230 PackedMcuVersion required_mcu_version;
231 ImageTransferProcessorFormat origin_format;
232 ImageTransferProcessorFormat trimming_format;
233 u16 trimming_start_x;
234 u16 trimming_start_y;
235 u8 is_external_light_filter_enabled;
236 INSERT_PADDING_BYTES(3);
237 };
238 static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
239 "PackedImageTransferProcessorExConfig is an invalid size");
240
241 // This is nn::irsensor::PackedIrLedProcessorConfig
242 struct PackedIrLedProcessorConfig {
243 PackedMcuVersion required_mcu_version;
244 u8 light_target;
245 INSERT_PADDING_BYTES(3);
246 };
247 static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
248 "PackedIrLedProcessorConfig is an invalid size");
249
20 void ActivateIrsensor(Kernel::HLERequestContext& ctx); 250 void ActivateIrsensor(Kernel::HLERequestContext& ctx);
21 void DeactivateIrsensor(Kernel::HLERequestContext& ctx); 251 void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
22 void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); 252 void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -35,8 +265,6 @@ private:
35 void RunIrLedProcessor(Kernel::HLERequestContext& ctx); 265 void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
36 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); 266 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
37 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); 267 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
38
39 const u32 device_handle{0xABCD};
40}; 268};
41 269
42class IRS_SYS final : public ServiceFramework<IRS_SYS> { 270class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
index 19bd85b6c..4ed3f02e2 100644
--- a/src/core/hle/service/jit/jit_context.cpp
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -11,10 +11,13 @@
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/div_ceil.h" 13#include "common/div_ceil.h"
14#include "common/elf.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "core/hle/service/jit/jit_context.h" 16#include "core/hle/service/jit/jit_context.h"
16#include "core/memory.h" 17#include "core/memory.h"
17 18
19using namespace Common::ELF;
20
18namespace Service::JIT { 21namespace Service::JIT {
19 22
20constexpr std::array<u8, 8> SVC0_ARM64 = { 23constexpr std::array<u8, 8> SVC0_ARM64 = {
@@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{
26 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", 29 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
27}; 30};
28 31
29struct Elf64_Dyn {
30 u64 d_tag;
31 u64 d_un;
32};
33
34struct Elf64_Rela {
35 u64 r_offset;
36 u64 r_info;
37 s64 r_addend;
38};
39
40static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
41 return static_cast<u32>(rela->r_info);
42}
43
44constexpr int DT_RELA = 7; /* Address of Rela relocs */
45constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
46constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
47
48constexpr size_t STACK_ALIGN = 16; 32constexpr size_t STACK_ALIGN = 16;
49 33
50class JITContextImpl; 34class JITContextImpl;
@@ -206,17 +190,17 @@ public:
206 if (!dyn.d_tag) { 190 if (!dyn.d_tag) {
207 break; 191 break;
208 } 192 }
209 if (dyn.d_tag == DT_RELA) { 193 if (dyn.d_tag == ElfDtRela) {
210 rela_dyn = dyn.d_un; 194 rela_dyn = dyn.d_un.d_ptr;
211 } 195 }
212 if (dyn.d_tag == DT_RELASZ) { 196 if (dyn.d_tag == ElfDtRelasz) {
213 num_rela = dyn.d_un / sizeof(Elf64_Rela); 197 num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
214 } 198 }
215 } 199 }
216 200
217 for (size_t i = 0; i < num_rela; i++) { 201 for (size_t i = 0; i < num_rela; i++) {
218 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; 202 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
219 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) { 203 if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
220 continue; 204 continue;
221 } 205 }
222 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; 206 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index fa72fcba9..72e4902cb 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -347,7 +347,7 @@ public:
347 } 347 }
348 348
349 if (!succeeded) { 349 if (!succeeded) {
350 UNREACHABLE_MSG("Out of address space!"); 350 ASSERT_MSG(false, "Out of address space!");
351 return Kernel::ResultOutOfMemory; 351 return Kernel::ResultOutOfMemory;
352 } 352 }
353 353
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 4964539f9..08300a1a6 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -290,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
290 u8 glasses_type{}; 290 u8 glasses_type{};
291 while (glasses_type_start < glasses_type_info.values[glasses_type]) { 291 while (glasses_type_start < glasses_type_info.values[glasses_type]) {
292 if (++glasses_type >= glasses_type_info.values_count) { 292 if (++glasses_type >= glasses_type_info.values_count) {
293 UNREACHABLE(); 293 ASSERT(false);
294 break; 294 break;
295 } 295 }
296 } 296 }
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
index f77f0df27..a6fa943e8 100644
--- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -23,7 +23,7 @@ u32 SyncpointManager::AllocateSyncpoint() {
23 return syncpoint_id; 23 return syncpoint_id;
24 } 24 }
25 } 25 }
26 UNREACHABLE_MSG("No more available syncpoints!"); 26 ASSERT_MSG(false, "No more available syncpoints!");
27 return {}; 27 return {};
28} 28}
29 29
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index d7db77aff..4b3d5efd6 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
89 89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); 90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91 91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to 92 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side. 93 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) { 94 if (out_buffer->acquire_called) {
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
139 ++current; 131 ++current;
140 } 132 }
141 133
142 if (slots[slot].buffer_state == BufferState::Acquired) { 134 slots[slot].buffer_state = BufferState::Free;
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152 135
153 slots[slot].needs_cleanup_on_release = false; 136 listener = core->connected_producer_listener;
154 137
155 return Status::StaleBufferSlot; 138 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162 139
163 core->SignalDequeueCondition(); 140 core->SignalDequeueCondition();
164 } 141 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index d4e8b44d0..ea4a14ea4 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
84 84
85 slots[slot].graphic_buffer.reset(); 85 slots[slot].graphic_buffer.reset();
86 86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free; 87 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX; 88 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false; 89 slots[slot].acquire_called = false;
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index fe95d1b73..337431488 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -659,7 +659,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
659 value = core->consumer_usage_bit; 659 value = core->consumer_usage_bit;
660 break; 660 break;
661 default: 661 default:
662 UNREACHABLE(); 662 ASSERT(false);
663 return Status::BadValue; 663 return Status::BadValue;
664 } 664 }
665 665
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
index 6b3e87446..0cd0e9964 100644
--- a/src/core/hle/service/nvflinger/buffer_slot.h
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -31,7 +31,6 @@ struct BufferSlot final {
31 u64 frame_number{}; 31 u64 frame_number{};
32 Fence fence; 32 Fence fence;
33 bool acquire_called{}; 33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{}; 34 bool attached_by_consumer{};
36 bool is_preallocated{}; 35 bool is_preallocated{};
37}; 36};
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index f0cc9a155..508091dc2 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -48,12 +48,12 @@ ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
48} 48}
49 49
50ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) { 50ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
51 UNREACHABLE(); 51 UNIMPLEMENTED();
52 return ERROR_NOT_IMPLEMENTED; 52 return ERROR_NOT_IMPLEMENTED;
53} 53}
54 54
55ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) { 55ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
56 UNREACHABLE(); 56 UNIMPLEMENTED();
57 return ERROR_NOT_IMPLEMENTED; 57 return ERROR_NOT_IMPLEMENTED;
58} 58}
59 59
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index acc038dbf..28667710e 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -111,7 +111,7 @@ struct TimeManager::Impl final {
111 FileSys::VirtualFile& vfs_file) { 111 FileSys::VirtualFile& vfs_file) {
112 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule( 112 if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
113 location_name, vfs_file) != ResultSuccess) { 113 location_name, vfs_file) != ResultSuccess) {
114 UNREACHABLE(); 114 ASSERT(false);
115 return; 115 return;
116 } 116 }
117 117
@@ -155,7 +155,7 @@ struct TimeManager::Impl final {
155 } else { 155 } else {
156 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) != 156 if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
157 ResultSuccess) { 157 ResultSuccess) {
158 UNREACHABLE(); 158 ASSERT(false);
159 return; 159 return;
160 } 160 }
161 } 161 }
@@ -170,7 +170,7 @@ struct TimeManager::Impl final {
170 170
171 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != 171 if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
172 ResultSuccess) { 172 ResultSuccess) {
173 UNREACHABLE(); 173 ASSERT(false);
174 return; 174 return;
175 } 175 }
176 176
@@ -183,7 +183,7 @@ struct TimeManager::Impl final {
183 Clock::SteadyClockTimePoint steady_clock_time_point) { 183 Clock::SteadyClockTimePoint steady_clock_time_point) {
184 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled( 184 if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
185 system_, is_automatic_correction_enabled) != ResultSuccess) { 185 system_, is_automatic_correction_enabled) != ResultSuccess) {
186 UNREACHABLE(); 186 ASSERT(false);
187 return; 187 return;
188 } 188 }
189 189
@@ -203,7 +203,7 @@ struct TimeManager::Impl final {
203 if (GetStandardLocalSystemClockCore() 203 if (GetStandardLocalSystemClockCore()
204 .SetCurrentTime(system_, timespan.ToSeconds()) 204 .SetCurrentTime(system_, timespan.ToSeconds())
205 .IsError()) { 205 .IsError()) {
206 UNREACHABLE(); 206 ASSERT(false);
207 return; 207 return;
208 } 208 }
209 } 209 }
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 449a5ac96..fee05ec7a 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -110,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) {
110 } 110 }
111} 111}
112 112
113static constexpr int GetMonthLength(bool is_leap_year, int month) { 113static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
114 constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 114 constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
115 constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 115 constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
116 31, 31, 30, 31, 30, 31};
117 return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; 116 return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
118} 117}
119 118
@@ -280,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
280 break; 279 break;
281 } 280 }
282 default: 281 default:
283 UNREACHABLE(); 282 ASSERT(false);
284 } 283 }
285 return value + rule.transition_time + offset; 284 return value + rule.transition_time + offset;
286} 285}
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index cf5933699..dfb10c34f 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/elf.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "core/hle/kernel/code_set.h" 11#include "core/hle/kernel/code_set.h"
11#include "core/hle/kernel/k_page_table.h" 12#include "core/hle/kernel/k_page_table.h"
@@ -13,159 +14,7 @@
13#include "core/loader/elf.h" 14#include "core/loader/elf.h"
14#include "core/memory.h" 15#include "core/memory.h"
15 16
16//////////////////////////////////////////////////////////////////////////////////////////////////// 17using namespace Common::ELF;
17// ELF Header Constants
18
19// File type
20enum ElfType {
21 ET_NONE = 0,
22 ET_REL = 1,
23 ET_EXEC = 2,
24 ET_DYN = 3,
25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF,
28};
29
30// Machine/Architecture
31enum ElfMachine {
32 EM_NONE = 0,
33 EM_M32 = 1,
34 EM_SPARC = 2,
35 EM_386 = 3,
36 EM_68K = 4,
37 EM_88K = 5,
38 EM_860 = 7,
39 EM_MIPS = 8
40};
41
42// File version
43#define EV_NONE 0
44#define EV_CURRENT 1
45
46// Identification index
47#define EI_MAG0 0
48#define EI_MAG1 1
49#define EI_MAG2 2
50#define EI_MAG3 3
51#define EI_CLASS 4
52#define EI_DATA 5
53#define EI_VERSION 6
54#define EI_PAD 7
55#define EI_NIDENT 16
56
57// Sections constants
58
59// Section types
60#define SHT_NULL 0
61#define SHT_PROGBITS 1
62#define SHT_SYMTAB 2
63#define SHT_STRTAB 3
64#define SHT_RELA 4
65#define SHT_HASH 5
66#define SHT_DYNAMIC 6
67#define SHT_NOTE 7
68#define SHT_NOBITS 8
69#define SHT_REL 9
70#define SHT_SHLIB 10
71#define SHT_DYNSYM 11
72#define SHT_LOPROC 0x70000000
73#define SHT_HIPROC 0x7FFFFFFF
74#define SHT_LOUSER 0x80000000
75#define SHT_HIUSER 0xFFFFFFFF
76
77// Section flags
78enum ElfSectionFlags {
79 SHF_WRITE = 0x1,
80 SHF_ALLOC = 0x2,
81 SHF_EXECINSTR = 0x4,
82 SHF_MASKPROC = 0xF0000000,
83};
84
85// Segment types
86#define PT_NULL 0
87#define PT_LOAD 1
88#define PT_DYNAMIC 2
89#define PT_INTERP 3
90#define PT_NOTE 4
91#define PT_SHLIB 5
92#define PT_PHDR 6
93#define PT_LOPROC 0x70000000
94#define PT_HIPROC 0x7FFFFFFF
95
96// Segment flags
97#define PF_X 0x1
98#define PF_W 0x2
99#define PF_R 0x4
100#define PF_MASKPROC 0xF0000000
101
102typedef unsigned int Elf32_Addr;
103typedef unsigned short Elf32_Half;
104typedef unsigned int Elf32_Off;
105typedef signed int Elf32_Sword;
106typedef unsigned int Elf32_Word;
107
108////////////////////////////////////////////////////////////////////////////////////////////////////
109// ELF file header
110
111struct Elf32_Ehdr {
112 unsigned char e_ident[EI_NIDENT];
113 Elf32_Half e_type;
114 Elf32_Half e_machine;
115 Elf32_Word e_version;
116 Elf32_Addr e_entry;
117 Elf32_Off e_phoff;
118 Elf32_Off e_shoff;
119 Elf32_Word e_flags;
120 Elf32_Half e_ehsize;
121 Elf32_Half e_phentsize;
122 Elf32_Half e_phnum;
123 Elf32_Half e_shentsize;
124 Elf32_Half e_shnum;
125 Elf32_Half e_shstrndx;
126};
127
128// Section header
129struct Elf32_Shdr {
130 Elf32_Word sh_name;
131 Elf32_Word sh_type;
132 Elf32_Word sh_flags;
133 Elf32_Addr sh_addr;
134 Elf32_Off sh_offset;
135 Elf32_Word sh_size;
136 Elf32_Word sh_link;
137 Elf32_Word sh_info;
138 Elf32_Word sh_addralign;
139 Elf32_Word sh_entsize;
140};
141
142// Segment header
143struct Elf32_Phdr {
144 Elf32_Word p_type;
145 Elf32_Off p_offset;
146 Elf32_Addr p_vaddr;
147 Elf32_Addr p_paddr;
148 Elf32_Word p_filesz;
149 Elf32_Word p_memsz;
150 Elf32_Word p_flags;
151 Elf32_Word p_align;
152};
153
154// Symbol table entry
155struct Elf32_Sym {
156 Elf32_Word st_name;
157 Elf32_Addr st_value;
158 Elf32_Word st_size;
159 unsigned char st_info;
160 unsigned char st_other;
161 Elf32_Half st_shndx;
162};
163
164// Relocation entries
165struct Elf32_Rel {
166 Elf32_Addr r_offset;
167 Elf32_Word r_info;
168};
169 18
170//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
171// ElfReader class 20// ElfReader class
@@ -193,11 +42,11 @@ public:
193 } 42 }
194 43
195 // Quick accessors 44 // Quick accessors
196 ElfType GetType() const { 45 u16 GetType() const {
197 return (ElfType)(header->e_type); 46 return header->e_type;
198 } 47 }
199 ElfMachine GetMachine() const { 48 u16 GetMachine() const {
200 return (ElfMachine)(header->e_machine); 49 return header->e_machine;
201 } 50 }
202 VAddr GetEntryPoint() const { 51 VAddr GetEntryPoint() const {
203 return entryPoint; 52 return entryPoint;
@@ -220,13 +69,13 @@ public:
220 const u8* GetSectionDataPtr(int section) const { 69 const u8* GetSectionDataPtr(int section) const {
221 if (section < 0 || section >= header->e_shnum) 70 if (section < 0 || section >= header->e_shnum)
222 return nullptr; 71 return nullptr;
223 if (sections[section].sh_type != SHT_NOBITS) 72 if (sections[section].sh_type != ElfShtNobits)
224 return GetPtr(sections[section].sh_offset); 73 return GetPtr(sections[section].sh_offset);
225 else 74 else
226 return nullptr; 75 return nullptr;
227 } 76 }
228 bool IsCodeSection(int section) const { 77 bool IsCodeSection(int section) const {
229 return sections[section].sh_type == SHT_PROGBITS; 78 return sections[section].sh_type == ElfShtProgBits;
230 } 79 }
231 const u8* GetSegmentPtr(int segment) { 80 const u8* GetSegmentPtr(int segment) {
232 return GetPtr(segments[segment].p_offset); 81 return GetPtr(segments[segment].p_offset);
@@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) {
256} 105}
257 106
258const char* ElfReader::GetSectionName(int section) const { 107const char* ElfReader::GetSectionName(int section) const {
259 if (sections[section].sh_type == SHT_NULL) 108 if (sections[section].sh_type == ElfShtNull)
260 return nullptr; 109 return nullptr;
261 110
262 int name_offset = sections[section].sh_name; 111 int name_offset = sections[section].sh_name;
@@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
272 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 121 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
273 122
274 // Should we relocate? 123 // Should we relocate?
275 relocate = (header->e_type != ET_EXEC); 124 relocate = (header->e_type != ElfTypeExec);
276 125
277 if (relocate) { 126 if (relocate) {
278 LOG_DEBUG(Loader, "Relocatable module"); 127 LOG_DEBUG(Loader, "Relocatable module");
@@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
288 u64 total_image_size = 0; 137 u64 total_image_size = 0;
289 for (unsigned int i = 0; i < header->e_phnum; ++i) { 138 for (unsigned int i = 0; i < header->e_phnum; ++i) {
290 const Elf32_Phdr* p = &segments[i]; 139 const Elf32_Phdr* p = &segments[i];
291 if (p->p_type == PT_LOAD) { 140 if (p->p_type == ElfPtLoad) {
292 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; 141 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
293 } 142 }
294 } 143 }
@@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
303 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, 152 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
304 p->p_vaddr, p->p_filesz, p->p_memsz); 153 p->p_vaddr, p->p_filesz, p->p_memsz);
305 154
306 if (p->p_type == PT_LOAD) { 155 if (p->p_type == ElfPtLoad) {
307 Kernel::CodeSet::Segment* codeset_segment; 156 Kernel::CodeSet::Segment* codeset_segment;
308 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 157 u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
309 if (permission_flags == (PF_R | PF_X)) { 158 if (permission_flags == (ElfPfRead | ElfPfExec)) {
310 codeset_segment = &codeset.CodeSegment(); 159 codeset_segment = &codeset.CodeSegment();
311 } else if (permission_flags == (PF_R)) { 160 } else if (permission_flags == (ElfPfRead)) {
312 codeset_segment = &codeset.RODataSegment(); 161 codeset_segment = &codeset.RODataSegment();
313 } else if (permission_flags == (PF_R | PF_W)) { 162 } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
314 codeset_segment = &codeset.DataSegment(); 163 codeset_segment = &codeset.DataSegment();
315 } else { 164 } else {
316 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 165 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 8a938aa83..8dd956fc6 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -128,11 +128,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
128 128
129 // Apply patches if necessary 129 // Apply patches if necessary
130 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { 130 if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
131 std::vector<u8> pi_header; 131 std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
132 pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), 132 std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
133 reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); 133 std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
134 pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(), 134 program_image.size());
135 program_image.data() + program_image.size());
136 135
137 pi_header = pm->PatchNSO(pi_header, nso_file.GetName()); 136 pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
138 137
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 28d30eee2..7534de01e 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -594,6 +594,19 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
594 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; 594 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
595} 595}
596 596
597bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
598 VAddr end = base + size;
599 VAddr page = Common::AlignDown(base, PAGE_SIZE);
600
601 for (; page < end; page += PAGE_SIZE) {
602 if (!IsValidVirtualAddress(page)) {
603 return false;
604 }
605 }
606
607 return true;
608}
609
597u8* Memory::GetPointer(VAddr vaddr) { 610u8* Memory::GetPointer(VAddr vaddr) {
598 return impl->GetPointer(vaddr); 611 return impl->GetPointer(vaddr);
599} 612}
diff --git a/src/core/memory.h b/src/core/memory.h
index b5721b740..58cc27b29 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -96,6 +96,17 @@ public:
96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const; 96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
97 97
98 /** 98 /**
99 * Checks whether or not the supplied range of addresses are all valid
100 * virtual addresses for the current process.
101 *
102 * @param base The address to begin checking.
103 * @param size The amount of bytes to check.
104 *
105 * @returns True if all bytes in the given range are valid, false otherwise.
106 */
107 [[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
108
109 /**
99 * Gets a pointer to the given address. 110 * Gets a pointer to the given address.
100 * 111 *
101 * @param vaddr Virtual address to retrieve a pointer to. 112 * @param vaddr Virtual address to retrieve a pointer to.
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 7a0b73eca..5cc99fbe4 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -25,7 +25,6 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
25 return memory.Read64(addr); 25 return memory.Read64(addr);
26 default: 26 default:
27 UNREACHABLE(); 27 UNREACHABLE();
28 return 0;
29 } 28 }
30} 29}
31 30
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index d4fa69a77..48e799cf5 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -44,7 +44,6 @@ else()
44 -Werror 44 -Werror
45 -Werror=conversion 45 -Werror=conversion
46 -Werror=ignored-qualifiers 46 -Werror=ignored-qualifiers
47 -Werror=shadow
48 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 47 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
49 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 48 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
50 -Werror=unused-variable 49 -Werror=unused-variable
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index a5c63e74a..446c027d3 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -13,11 +13,11 @@
13namespace InputCommon { 13namespace InputCommon {
14 14
15namespace { 15namespace {
16std::string GetGUID(SDL_Joystick* joystick) { 16Common::UUID GetGUID(SDL_Joystick* joystick) {
17 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); 17 const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
18 char guid_str[33]; 18 std::array<u8, 16> data{};
19 SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str)); 19 std::memcpy(data.data(), guid.data, sizeof(data));
20 return guid_str; 20 return Common::UUID{data};
21} 21}
22} // Anonymous namespace 22} // Anonymous namespace
23 23
@@ -31,9 +31,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
31 31
32class SDLJoystick { 32class SDLJoystick {
33public: 33public:
34 SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick, 34 SDLJoystick(Common::UUID guid_, int port_, SDL_Joystick* joystick,
35 SDL_GameController* game_controller) 35 SDL_GameController* game_controller)
36 : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose}, 36 : guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
37 sdl_controller{game_controller, &SDL_GameControllerClose} { 37 sdl_controller{game_controller, &SDL_GameControllerClose} {
38 EnableMotion(); 38 EnableMotion();
39 } 39 }
@@ -120,7 +120,7 @@ public:
120 */ 120 */
121 const PadIdentifier GetPadIdentifier() const { 121 const PadIdentifier GetPadIdentifier() const {
122 return { 122 return {
123 .guid = Common::UUID{guid}, 123 .guid = guid,
124 .port = static_cast<std::size_t>(port), 124 .port = static_cast<std::size_t>(port),
125 .pad = 0, 125 .pad = 0,
126 }; 126 };
@@ -129,7 +129,7 @@ public:
129 /** 129 /**
130 * The guid of the joystick 130 * The guid of the joystick
131 */ 131 */
132 const std::string& GetGUID() const { 132 const Common::UUID& GetGUID() const {
133 return guid; 133 return guid;
134 } 134 }
135 135
@@ -228,7 +228,7 @@ public:
228 } 228 }
229 229
230private: 230private:
231 std::string guid; 231 Common::UUID guid;
232 int port; 232 int port;
233 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick; 233 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
234 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller; 234 std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
@@ -240,7 +240,7 @@ private:
240 BasicMotion motion; 240 BasicMotion motion;
241}; 241};
242 242
243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) { 243std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const Common::UUID& guid, int port) {
244 std::scoped_lock lock{joystick_map_mutex}; 244 std::scoped_lock lock{joystick_map_mutex};
245 const auto it = joystick_map.find(guid); 245 const auto it = joystick_map.find(guid);
246 246
@@ -259,9 +259,13 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string&
259 return joystick_map[guid].emplace_back(std::move(joystick)); 259 return joystick_map[guid].emplace_back(std::move(joystick));
260} 260}
261 261
262std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
263 return GetSDLJoystickByGUID(Common::UUID{guid}, port);
264}
265
262std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) { 266std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
263 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id); 267 auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
264 const std::string guid = GetGUID(sdl_joystick); 268 const auto guid = GetGUID(sdl_joystick);
265 269
266 std::scoped_lock lock{joystick_map_mutex}; 270 std::scoped_lock lock{joystick_map_mutex};
267 const auto map_it = joystick_map.find(guid); 271 const auto map_it = joystick_map.find(guid);
@@ -295,7 +299,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
295 return; 299 return;
296 } 300 }
297 301
298 const std::string guid = GetGUID(sdl_joystick); 302 const auto guid = GetGUID(sdl_joystick);
299 303
300 std::scoped_lock lock{joystick_map_mutex}; 304 std::scoped_lock lock{joystick_map_mutex};
301 if (joystick_map.find(guid) == joystick_map.end()) { 305 if (joystick_map.find(guid) == joystick_map.end()) {
@@ -324,7 +328,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
324} 328}
325 329
326void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) { 330void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
327 const std::string guid = GetGUID(sdl_joystick); 331 const auto guid = GetGUID(sdl_joystick);
328 332
329 std::scoped_lock lock{joystick_map_mutex}; 333 std::scoped_lock lock{joystick_map_mutex};
330 // This call to guid is safe since the joystick is guaranteed to be in the map 334 // This call to guid is safe since the joystick is guaranteed to be in the map
@@ -434,6 +438,7 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
434 using namespace std::chrono_literals; 438 using namespace std::chrono_literals;
435 while (initialized) { 439 while (initialized) {
436 SDL_PumpEvents(); 440 SDL_PumpEvents();
441 SendVibrations();
437 std::this_thread::sleep_for(1ms); 442 std::this_thread::sleep_for(1ms);
438 } 443 }
439 }); 444 });
@@ -469,7 +474,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
469 devices.emplace_back(Common::ParamPackage{ 474 devices.emplace_back(Common::ParamPackage{
470 {"engine", GetEngineName()}, 475 {"engine", GetEngineName()},
471 {"display", std::move(name)}, 476 {"display", std::move(name)},
472 {"guid", joystick->GetGUID()}, 477 {"guid", joystick->GetGUID().RawString()},
473 {"port", std::to_string(joystick->GetPort())}, 478 {"port", std::to_string(joystick->GetPort())},
474 }); 479 });
475 if (joystick->IsJoyconLeft()) { 480 if (joystick->IsJoyconLeft()) {
@@ -492,8 +497,8 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
492 devices.emplace_back(Common::ParamPackage{ 497 devices.emplace_back(Common::ParamPackage{
493 {"engine", GetEngineName()}, 498 {"engine", GetEngineName()},
494 {"display", std::move(name)}, 499 {"display", std::move(name)},
495 {"guid", joystick->GetGUID()}, 500 {"guid", joystick->GetGUID().RawString()},
496 {"guid2", joystick2->GetGUID()}, 501 {"guid2", joystick2->GetGUID().RawString()},
497 {"port", std::to_string(joystick->GetPort())}, 502 {"port", std::to_string(joystick->GetPort())},
498 }); 503 });
499 } 504 }
@@ -531,57 +536,75 @@ Common::Input::VibrationError SDLDriver::SetRumble(
531 .type = Common::Input::VibrationAmplificationType::Exponential, 536 .type = Common::Input::VibrationAmplificationType::Exponential,
532 }; 537 };
533 538
534 if (!joystick->RumblePlay(new_vibration)) { 539 if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
535 return Common::Input::VibrationError::Unknown; 540 if (!joystick->RumblePlay(new_vibration)) {
541 return Common::Input::VibrationError::Unknown;
542 }
543 return Common::Input::VibrationError::None;
536 } 544 }
537 545
546 vibration_queue.Push(VibrationRequest{
547 .identifier = identifier,
548 .vibration = new_vibration,
549 });
550
538 return Common::Input::VibrationError::None; 551 return Common::Input::VibrationError::None;
539} 552}
540 553
541Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid, 554void SDLDriver::SendVibrations() {
555 while (!vibration_queue.Empty()) {
556 VibrationRequest request;
557 vibration_queue.Pop(request);
558 const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
559 static_cast<int>(request.identifier.port));
560 joystick->RumblePlay(request.vibration);
561 }
562}
563
564Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
542 s32 axis, float value) const { 565 s32 axis, float value) const {
543 Common::ParamPackage params{}; 566 Common::ParamPackage params{};
544 params.Set("engine", GetEngineName()); 567 params.Set("engine", GetEngineName());
545 params.Set("port", port); 568 params.Set("port", port);
546 params.Set("guid", std::move(guid)); 569 params.Set("guid", guid.RawString());
547 params.Set("axis", axis); 570 params.Set("axis", axis);
548 params.Set("threshold", "0.5"); 571 params.Set("threshold", "0.5");
549 params.Set("invert", value < 0 ? "-" : "+"); 572 params.Set("invert", value < 0 ? "-" : "+");
550 return params; 573 return params;
551} 574}
552 575
553Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid, 576Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
554 s32 button) const { 577 s32 button) const {
555 Common::ParamPackage params{}; 578 Common::ParamPackage params{};
556 params.Set("engine", GetEngineName()); 579 params.Set("engine", GetEngineName());
557 params.Set("port", port); 580 params.Set("port", port);
558 params.Set("guid", std::move(guid)); 581 params.Set("guid", guid.RawString());
559 params.Set("button", button); 582 params.Set("button", button);
560 return params; 583 return params;
561} 584}
562 585
563Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat, 586Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, const Common::UUID& guid,
564 u8 value) const { 587 s32 hat, u8 value) const {
565 Common::ParamPackage params{}; 588 Common::ParamPackage params{};
566 params.Set("engine", GetEngineName()); 589 params.Set("engine", GetEngineName());
567 params.Set("port", port); 590 params.Set("port", port);
568 params.Set("guid", std::move(guid)); 591 params.Set("guid", guid.RawString());
569 params.Set("hat", hat); 592 params.Set("hat", hat);
570 params.Set("direction", GetHatButtonName(value)); 593 params.Set("direction", GetHatButtonName(value));
571 return params; 594 return params;
572} 595}
573 596
574Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const { 597Common::ParamPackage SDLDriver::BuildMotionParam(int port, const Common::UUID& guid) const {
575 Common::ParamPackage params{}; 598 Common::ParamPackage params{};
576 params.Set("engine", GetEngineName()); 599 params.Set("engine", GetEngineName());
577 params.Set("motion", 0); 600 params.Set("motion", 0);
578 params.Set("port", port); 601 params.Set("port", port);
579 params.Set("guid", std::move(guid)); 602 params.Set("guid", guid.RawString());
580 return params; 603 return params;
581} 604}
582 605
583Common::ParamPackage SDLDriver::BuildParamPackageForBinding( 606Common::ParamPackage SDLDriver::BuildParamPackageForBinding(
584 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const { 607 int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const {
585 switch (binding.bindType) { 608 switch (binding.bindType) {
586 case SDL_CONTROLLER_BINDTYPE_NONE: 609 case SDL_CONTROLLER_BINDTYPE_NONE:
587 break; 610 break;
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index dcd0d1e64..0846fbb50 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -12,6 +12,7 @@
12#include <SDL.h> 12#include <SDL.h>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/threadsafe_queue.h"
15#include "input_common/input_engine.h" 16#include "input_common/input_engine.h"
16 17
17union SDL_Event; 18union SDL_Event;
@@ -46,6 +47,7 @@ public:
46 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so 47 * Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
47 * tie it to a SDLJoystick with the same guid and that port 48 * tie it to a SDLJoystick with the same guid and that port
48 */ 49 */
50 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port);
49 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port); 51 std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
50 52
51 std::vector<Common::ParamPackage> GetInputDevices() const override; 53 std::vector<Common::ParamPackage> GetInputDevices() const override;
@@ -64,24 +66,32 @@ public:
64 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 66 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
65 67
66private: 68private:
69 struct VibrationRequest {
70 PadIdentifier identifier;
71 Common::Input::VibrationStatus vibration;
72 };
73
67 void InitJoystick(int joystick_index); 74 void InitJoystick(int joystick_index);
68 void CloseJoystick(SDL_Joystick* sdl_joystick); 75 void CloseJoystick(SDL_Joystick* sdl_joystick);
69 76
70 /// Needs to be called before SDL_QuitSubSystem. 77 /// Needs to be called before SDL_QuitSubSystem.
71 void CloseJoysticks(); 78 void CloseJoysticks();
72 79
73 Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis, 80 /// Takes all vibrations from the queue and sends the command to the controller
74 float value = 0.1f) const; 81 void SendVibrations();
75 Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, 82
83 Common::ParamPackage BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
84 s32 axis, float value = 0.1f) const;
85 Common::ParamPackage BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
76 s32 button) const; 86 s32 button) const;
77 87
78 Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, 88 Common::ParamPackage BuildHatParamPackageForButton(int port, const Common::UUID& guid, s32 hat,
79 u8 value) const; 89 u8 value) const;
80 90
81 Common::ParamPackage BuildMotionParam(int port, std::string guid) const; 91 Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
82 92
83 Common::ParamPackage BuildParamPackageForBinding( 93 Common::ParamPackage BuildParamPackageForBinding(
84 int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const; 94 int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const;
85 95
86 Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x, 96 Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
87 int axis_y, float offset_x, 97 int axis_y, float offset_x,
@@ -107,8 +117,11 @@ private:
107 /// Returns true if the button is on the left joycon 117 /// Returns true if the button is on the left joycon
108 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const; 118 bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
109 119
120 /// Queue of vibration request to controllers
121 Common::SPSCQueue<VibrationRequest> vibration_queue;
122
110 /// Map of GUID of a list of corresponding virtual Joysticks 123 /// Map of GUID of a list of corresponding virtual Joysticks
111 std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map; 124 std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
112 std::mutex joystick_map_mutex; 125 std::mutex joystick_map_mutex;
113 126
114 bool start_thread = false; 127 bool start_thread = false;
diff --git a/src/input_common/drivers/touch_screen.cpp b/src/input_common/drivers/touch_screen.cpp
index 8acbe4584..1753e0893 100644
--- a/src/input_common/drivers/touch_screen.cpp
+++ b/src/input_common/drivers/touch_screen.cpp
@@ -14,38 +14,93 @@ constexpr PadIdentifier identifier = {
14 14
15TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) { 15TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
16 PreSetController(identifier); 16 PreSetController(identifier);
17 ReleaseAllTouch();
17} 18}
18 19
19void TouchScreen::TouchMoved(float x, float y, std::size_t finger) { 20void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
20 if (finger >= 16) { 21 const auto index = GetIndexFromFingerId(finger_id);
22 if (!index) {
23 // Touch doesn't exist handle it as a new one
24 TouchPressed(x, y, finger_id);
21 return; 25 return;
22 } 26 }
23 TouchPressed(x, y, finger); 27 const auto i = index.value();
28 fingers[i].is_active = true;
29 SetButton(identifier, static_cast<int>(i), true);
30 SetAxis(identifier, static_cast<int>(i * 2), x);
31 SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
24} 32}
25 33
26void TouchScreen::TouchPressed(float x, float y, std::size_t finger) { 34void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
27 if (finger >= 16) { 35 if (GetIndexFromFingerId(finger_id)) {
36 // Touch already exist. Just update the data
37 TouchMoved(x, y, finger_id);
28 return; 38 return;
29 } 39 }
30 SetButton(identifier, static_cast<int>(finger), true); 40 const auto index = GetNextFreeIndex();
31 SetAxis(identifier, static_cast<int>(finger * 2), x); 41 if (!index) {
32 SetAxis(identifier, static_cast<int>(finger * 2 + 1), y); 42 // No free entries. Ignore input
43 return;
44 }
45 const auto i = index.value();
46 fingers[i].is_enabled = true;
47 fingers[i].finger_id = finger_id;
48 TouchMoved(x, y, finger_id);
33} 49}
34 50
35void TouchScreen::TouchReleased(std::size_t finger) { 51void TouchScreen::TouchReleased(std::size_t finger_id) {
36 if (finger >= 16) { 52 const auto index = GetIndexFromFingerId(finger_id);
53 if (!index) {
37 return; 54 return;
38 } 55 }
39 SetButton(identifier, static_cast<int>(finger), false); 56 const auto i = index.value();
40 SetAxis(identifier, static_cast<int>(finger * 2), 0.0f); 57 fingers[i].is_enabled = false;
41 SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f); 58 SetButton(identifier, static_cast<int>(i), false);
59 SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
60 SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
61}
62
63std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
64 for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
65 const auto& finger = fingers[index];
66 if (!finger.is_enabled) {
67 continue;
68 }
69 if (finger.finger_id == finger_id) {
70 return index;
71 }
72 }
73 return std::nullopt;
74}
75
76std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
77 for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
78 if (!fingers[index].is_enabled) {
79 return index;
80 }
81 }
82 return std::nullopt;
83}
84
85void TouchScreen::ClearActiveFlag() {
86 for (auto& finger : fingers) {
87 finger.is_active = false;
88 }
89}
90
91void TouchScreen::ReleaseInactiveTouch() {
92 for (const auto& finger : fingers) {
93 if (!finger.is_active) {
94 TouchReleased(finger.finger_id);
95 }
96 }
42} 97}
43 98
44void TouchScreen::ReleaseAllTouch() { 99void TouchScreen::ReleaseAllTouch() {
45 for (int index = 0; index < 16; ++index) { 100 for (const auto& finger : fingers) {
46 SetButton(identifier, index, false); 101 if (finger.is_enabled) {
47 SetAxis(identifier, index * 2, 0.0f); 102 TouchReleased(finger.finger_id);
48 SetAxis(identifier, index * 2 + 1, 0.0f); 103 }
49 } 104 }
50} 105}
51 106
diff --git a/src/input_common/drivers/touch_screen.h b/src/input_common/drivers/touch_screen.h
index 193478ead..f46036ffd 100644
--- a/src/input_common/drivers/touch_screen.h
+++ b/src/input_common/drivers/touch_screen.h
@@ -3,41 +3,65 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <optional>
7
6#include "input_common/input_engine.h" 8#include "input_common/input_engine.h"
7 9
8namespace InputCommon { 10namespace InputCommon {
9 11
10/** 12/**
11 * A button device factory representing a keyboard. It receives keyboard events and forward them 13 * A touch device factory representing a touch screen. It receives touch events and forward them
12 * to all button devices it created. 14 * to all touch devices it created.
13 */ 15 */
14class TouchScreen final : public InputEngine { 16class TouchScreen final : public InputEngine {
15public: 17public:
16 explicit TouchScreen(std::string input_engine_); 18 explicit TouchScreen(std::string input_engine_);
17 19
18 /** 20 /**
19 * Signals that mouse has moved. 21 * Signals that touch has moved and marks this touch point as active
20 * @param x the x-coordinate of the cursor 22 * @param x new horizontal position
21 * @param y the y-coordinate of the cursor 23 * @param y new vertical position
22 * @param center_x the x-coordinate of the middle of the screen 24 * @param finger_id of the touch point to be updated
23 * @param center_y the y-coordinate of the middle of the screen
24 */ 25 */
25 void TouchMoved(float x, float y, std::size_t finger); 26 void TouchMoved(float x, float y, std::size_t finger_id);
26 27
27 /** 28 /**
28 * Sets the status of all buttons bound with the key to pressed 29 * Signals and creates a new touch point with this finger id
29 * @param key_code the code of the key to press 30 * @param x starting horizontal position
31 * @param y starting vertical position
32 * @param finger_id to be assigned to the new touch point
30 */ 33 */
31 void TouchPressed(float x, float y, std::size_t finger); 34 void TouchPressed(float x, float y, std::size_t finger_id);
32 35
33 /** 36 /**
34 * Sets the status of all buttons bound with the key to released 37 * Signals and resets the touch point related to the this finger id
35 * @param key_code the code of the key to release 38 * @param finger_id to be released
36 */ 39 */
37 void TouchReleased(std::size_t finger); 40 void TouchReleased(std::size_t finger_id);
41
42 /// Resets the active flag for each touch point
43 void ClearActiveFlag();
44
45 /// Releases all touch that haven't been marked as active
46 void ReleaseInactiveTouch();
38 47
39 /// Resets all inputs to their initial value 48 /// Resets all inputs to their initial value
40 void ReleaseAllTouch(); 49 void ReleaseAllTouch();
50
51private:
52 static constexpr std::size_t MAX_FINGER_COUNT = 16;
53
54 struct TouchStatus {
55 std::size_t finger_id{};
56 bool is_enabled{};
57 bool is_active{};
58 };
59
60 std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
61
62 std::optional<std::size_t> GetNextFreeIndex() const;
63
64 std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
41}; 65};
42 66
43} // namespace InputCommon 67} // namespace InputCommon
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 4c76ce1ea..ae1dbe619 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -253,9 +253,6 @@ else()
253 -Werror 253 -Werror
254 -Werror=conversion 254 -Werror=conversion
255 -Werror=ignored-qualifiers 255 -Werror=ignored-qualifiers
256 -Werror=implicit-fallthrough
257 -Werror=shadow
258 -Werror=sign-compare
259 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter> 256 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
260 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 257 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
261 -Werror=unused-variable 258 -Werror=unused-variable
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index d17dc0376..752879a18 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -103,6 +103,6 @@ struct fmt::formatter<Shader::IR::Opcode> {
103 } 103 }
104 template <typename FormatContext> 104 template <typename FormatContext>
105 auto format(const Shader::IR::Opcode& op, FormatContext& ctx) { 105 auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
106 return format_to(ctx.out(), "{}", Shader::IR::NameOf(op)); 106 return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
107 } 107 }
108}; 108};
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
index 2487b9b0b..1ce45b3a5 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -58,7 +58,7 @@ public:
58 [[nodiscard]] Stack Remove(Token token) const; 58 [[nodiscard]] Stack Remove(Token token) const;
59 59
60private: 60private:
61 boost::container::small_vector<StackEntry, 3> entries; 61 std::vector<StackEntry> entries;
62}; 62};
63 63
64struct IndirectBranch { 64struct IndirectBranch {
diff --git a/src/shader_recompiler/frontend/maxwell/opcodes.h b/src/shader_recompiler/frontend/maxwell/opcodes.h
index 83093fca0..72dd143c2 100644
--- a/src/shader_recompiler/frontend/maxwell/opcodes.h
+++ b/src/shader_recompiler/frontend/maxwell/opcodes.h
@@ -24,6 +24,6 @@ struct fmt::formatter<Shader::Maxwell::Opcode> {
24 } 24 }
25 template <typename FormatContext> 25 template <typename FormatContext>
26 auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) { 26 auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
27 return format_to(ctx.out(), "{}", NameOf(opcode)); 27 return fmt::format_to(ctx.out(), "{}", NameOf(opcode));
28 } 28 }
29}; 29};
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 3dc7c9a11..578bc8c1b 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -975,13 +975,7 @@ private:
975 Environment& env; 975 Environment& env;
976 IR::AbstractSyntaxList& syntax_list; 976 IR::AbstractSyntaxList& syntax_list;
977 bool uses_demote_to_helper{}; 977 bool uses_demote_to_helper{};
978
979// TODO: C++20 Remove this when all compilers support constexpr std::vector
980#if __cpp_lib_constexpr_vector >= 201907
981 static constexpr Flow::Block dummy_flow_block;
982#else
983 const Flow::Block dummy_flow_block; 978 const Flow::Block dummy_flow_block;
984#endif
985}; 979};
986} // Anonymous namespace 980} // Anonymous namespace
987 981
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 6a6325e38..14de7bc89 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -258,10 +258,6 @@ if (MSVC)
258 target_compile_options(video_core PRIVATE 258 target_compile_options(video_core PRIVATE
259 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 259 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
260 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data 260 /we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
261 /we4456 # Declaration of 'identifier' hides previous local declaration
262 /we4457 # Declaration of 'identifier' hides function parameter
263 /we4458 # Declaration of 'identifier' hides class member
264 /we4459 # Declaration of 'identifier' hides global declaration
265 ) 261 )
266else() 262else()
267 target_compile_options(video_core PRIVATE 263 target_compile_options(video_core PRIVATE
@@ -269,7 +265,6 @@ else()
269 -Wno-error=sign-conversion 265 -Wno-error=sign-conversion
270 -Werror=pessimizing-move 266 -Werror=pessimizing-move
271 -Werror=redundant-move 267 -Werror=redundant-move
272 -Werror=shadow
273 -Werror=type-limits 268 -Werror=type-limits
274 269
275 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess> 270 $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
@@ -277,3 +272,7 @@ else()
277 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 272 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
278 ) 273 )
279endif() 274endif()
275
276if (ARCHITECTURE_x86_64)
277 target_link_libraries(video_core PRIVATE dynarmic)
278endif()
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
index 83b2e0fc4..a5eb97b7f 100644
--- a/src/video_core/command_classes/codecs/codec.cpp
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -224,7 +224,7 @@ void Codec::Decode() {
224 vp9_hidden_frame = vp9_decoder->WasFrameHidden(); 224 vp9_hidden_frame = vp9_decoder->WasFrameHidden();
225 return vp9_decoder->GetFrameBytes(); 225 return vp9_decoder->GetFrameBytes();
226 default: 226 default:
227 UNREACHABLE(); 227 ASSERT(false);
228 return std::vector<u8>{}; 228 return std::vector<u8>{};
229 } 229 }
230 }(); 230 }();
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index a95618913..c01431441 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -153,7 +153,7 @@ constexpr Vp9EntropyProbs default_probs{
153 .high_precision{128, 128}, 153 .high_precision{128, 128},
154}; 154};
155 155
156constexpr std::array<s32, 256> norm_lut{ 156constexpr std::array<u8, 256> norm_lut{
157 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 157 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
158 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 158 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
159 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 159 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -164,7 +164,7 @@ constexpr std::array<s32, 256> norm_lut{
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165}; 165};
166 166
167constexpr std::array<s32, 254> map_lut{ 167constexpr std::array<u8, 254> map_lut{
168 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 168 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
169 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54, 169 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
170 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 170 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
@@ -232,7 +232,7 @@ constexpr std::array<s32, 254> map_lut{
232 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1)); 232 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
233 } 233 }
234 234
235 return map_lut[index]; 235 return static_cast<s32>(map_lut[index]);
236} 236}
237} // Anonymous namespace 237} // Anonymous namespace
238 238
@@ -819,7 +819,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
819 local_range = range - split; 819 local_range = range - split;
820 } 820 }
821 821
822 s32 shift = norm_lut[local_range]; 822 s32 shift = static_cast<s32>(norm_lut[local_range]);
823 local_range <<= shift; 823 local_range <<= shift;
824 count += shift; 824 count += shift;
825 825
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index bef321b6e..7c17df353 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -228,7 +228,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
228 break; 228 break;
229 } 229 }
230 default: 230 default:
231 UNREACHABLE(); 231 ASSERT(false);
232 break; 232 break;
233 } 233 }
234 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(), 234 gpu.MemoryManager().WriteBlock(output_surface_chroma_address, chroma_buffer.data(),
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d4652b167..3a4646289 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -173,6 +173,8 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
173 case MAXWELL3D_REG_INDEX(shadow_ram_control): 173 case MAXWELL3D_REG_INDEX(shadow_ram_control):
174 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument); 174 shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
175 return; 175 return;
176 case MAXWELL3D_REG_INDEX(macros.upload_address):
177 return macro_engine->ClearCode(regs.macros.upload_address);
176 case MAXWELL3D_REG_INDEX(macros.data): 178 case MAXWELL3D_REG_INDEX(macros.data):
177 return macro_engine->AddCode(regs.macros.upload_address, argument); 179 return macro_engine->AddCode(regs.macros.upload_address, argument);
178 case MAXWELL3D_REG_INDEX(macros.bind): 180 case MAXWELL3D_REG_INDEX(macros.bind):
@@ -593,8 +595,8 @@ void Maxwell3D::DrawArrays() {
593 595
594std::optional<u64> Maxwell3D::GetQueryResult() { 596std::optional<u64> Maxwell3D::GetQueryResult() {
595 switch (regs.query.query_get.select) { 597 switch (regs.query.query_get.select) {
596 case Regs::QuerySelect::Zero: 598 case Regs::QuerySelect::Payload:
597 return 0; 599 return regs.query.query_sequence;
598 case Regs::QuerySelect::SamplesPassed: 600 case Regs::QuerySelect::SamplesPassed:
599 // Deferred. 601 // Deferred.
600 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, 602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c0c2c7d96..5f9eb208c 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -93,7 +93,7 @@ public:
93 }; 93 };
94 94
95 enum class QuerySelect : u32 { 95 enum class QuerySelect : u32 {
96 Zero = 0, 96 Payload = 0,
97 TimeElapsed = 2, 97 TimeElapsed = 2,
98 TransformFeedbackPrimitivesGenerated = 11, 98 TransformFeedbackPrimitivesGenerated = 11,
99 PrimitivesGenerated = 18, 99 PrimitivesGenerated = 18,
@@ -202,7 +202,7 @@ public:
202 case Size::Size_11_11_10: 202 case Size::Size_11_11_10:
203 return 3; 203 return 3;
204 default: 204 default:
205 UNREACHABLE(); 205 ASSERT(false);
206 return 1; 206 return 1;
207 } 207 }
208 } 208 }
@@ -238,7 +238,7 @@ public:
238 case Size::Size_11_11_10: 238 case Size::Size_11_11_10:
239 return 4; 239 return 4;
240 default: 240 default:
241 UNREACHABLE(); 241 ASSERT(false);
242 return 1; 242 return 1;
243 } 243 }
244 } 244 }
@@ -274,7 +274,7 @@ public:
274 case Size::Size_11_11_10: 274 case Size::Size_11_11_10:
275 return "11_11_10"; 275 return "11_11_10";
276 default: 276 default:
277 UNREACHABLE(); 277 ASSERT(false);
278 return {}; 278 return {};
279 } 279 }
280 } 280 }
@@ -296,7 +296,7 @@ public:
296 case Type::Float: 296 case Type::Float:
297 return "FLOAT"; 297 return "FLOAT";
298 } 298 }
299 UNREACHABLE(); 299 ASSERT(false);
300 return {}; 300 return {};
301 } 301 }
302 302
@@ -336,7 +336,7 @@ public:
336 case 3: 336 case 3:
337 return {x3, y3}; 337 return {x3, y3};
338 default: 338 default:
339 UNREACHABLE(); 339 ASSERT(false);
340 return {0, 0}; 340 return {0, 0};
341 } 341 }
342 } 342 }
@@ -1193,7 +1193,7 @@ public:
1193 case IndexFormat::UnsignedInt: 1193 case IndexFormat::UnsignedInt:
1194 return 4; 1194 return 4;
1195 } 1195 }
1196 UNREACHABLE(); 1196 ASSERT(false);
1197 return 1; 1197 return 1;
1198 } 1198 }
1199 1199
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 76e8bc656..0efe58282 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -62,7 +62,7 @@ void MaxwellDMA::Launch() {
62 62
63 if (!is_src_pitch && !is_dst_pitch) { 63 if (!is_src_pitch && !is_dst_pitch) {
64 // If both the source and the destination are in block layout, assert. 64 // If both the source and the destination are in block layout, assert.
65 UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented"); 65 UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
66 return; 66 return;
67 } 67 }
68 68
@@ -134,7 +134,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
134 134
135 // Deswizzle the input and copy it over. 135 // Deswizzle the input and copy it over.
136 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); 136 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
137 const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; 137 const u32 bytes_per_pixel =
138 regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
138 const Parameters& src_params = regs.src_params; 139 const Parameters& src_params = regs.src_params;
139 const u32 width = src_params.width; 140 const u32 width = src_params.width;
140 const u32 height = src_params.height; 141 const u32 height = src_params.height;
@@ -166,7 +167,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
166 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0); 167 UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
167 168
168 const auto& dst_params = regs.dst_params; 169 const auto& dst_params = regs.dst_params;
169 const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in; 170 const u32 bytes_per_pixel =
171 regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1;
170 const u32 width = dst_params.width; 172 const u32 width = dst_params.width;
171 const u32 height = dst_params.height; 173 const u32 height = dst_params.height;
172 const u32 depth = dst_params.depth; 174 const u32 depth = dst_params.depth;
@@ -210,7 +212,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
210} 212}
211 213
212void MaxwellDMA::FastCopyBlockLinearToPitch() { 214void MaxwellDMA::FastCopyBlockLinearToPitch() {
213 const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in; 215 const u32 bytes_per_pixel =
216 regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
214 const size_t src_size = GOB_SIZE; 217 const size_t src_size = GOB_SIZE;
215 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count; 218 const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
216 u32 pos_x = regs.src_params.origin.x; 219 u32 pos_x = regs.src_params.origin.x;
@@ -257,7 +260,7 @@ void MaxwellDMA::ReleaseSemaphore() {
257 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks()); 260 memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
258 break; 261 break;
259 default: 262 default:
260 UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value())); 263 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
261 } 264 }
262} 265}
263 266
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index b79a73132..b0ce9f000 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -31,7 +31,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
31 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); 31 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
32 32
33 while (!stop_token.stop_requested()) { 33 while (!stop_token.stop_requested()) {
34 CommandDataContainer next = state.queue.PopWait(stop_token); 34 CommandDataContainer next;
35 state.queue.Pop(next, stop_token);
35 if (stop_token.stop_requested()) { 36 if (stop_token.stop_requested()) {
36 break; 37 break;
37 } 38 }
@@ -49,7 +50,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
49 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { 50 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
50 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); 51 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
51 } else { 52 } else {
52 UNREACHABLE(); 53 ASSERT(false);
53 } 54 }
54 state.signaled_fence.store(next.fence); 55 state.signaled_fence.store(next.fence);
55 if (next.block) { 56 if (next.block) {
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 71cd35756..ad9fd5eff 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,7 +10,7 @@
10#include <thread> 10#include <thread>
11#include <variant> 11#include <variant>
12 12
13#include "common/threadsafe_queue.h" 13#include "common/bounded_threadsafe_queue.h"
14#include "video_core/framebuffer_config.h" 14#include "video_core/framebuffer_config.h"
15 15
16namespace Tegra { 16namespace Tegra {
@@ -96,9 +96,9 @@ struct CommandDataContainer {
96 96
97/// Struct used to synchronize the GPU thread 97/// Struct used to synchronize the GPU thread
98struct SynchState final { 98struct SynchState final {
99 using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>; 99 using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
100 std::mutex write_lock; 100 std::mutex write_lock;
101 CommandQueue queue; 101 CommandQueue queue{512}; // size must be 2^n
102 u64 last_fence{}; 102 u64 last_fence{};
103 std::atomic<u64> signaled_fence{}; 103 std::atomic<u64> signaled_fence{};
104 std::condition_variable_any cv; 104 std::condition_variable_any cv;
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index a033d03be..43f8b5904 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -2,11 +2,15 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstring> 4#include <cstring>
5#include <fstream>
5#include <optional> 6#include <optional>
7#include <span>
6 8
7#include <boost/container_hash/hash.hpp> 9#include <boost/container_hash/hash.hpp>
8 10
9#include "common/assert.h" 11#include "common/assert.h"
12#include "common/fs/fs.h"
13#include "common/fs/path_util.h"
10#include "common/settings.h" 14#include "common/settings.h"
11#include "video_core/macro/macro.h" 15#include "video_core/macro/macro.h"
12#include "video_core/macro/macro_hle.h" 16#include "video_core/macro/macro_hle.h"
@@ -15,6 +19,23 @@
15 19
16namespace Tegra { 20namespace Tegra {
17 21
22static void Dump(u64 hash, std::span<const u32> code) {
23 const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
24 const auto macro_dir{base_dir / "macros"};
25 if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
26 LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
27 return;
28 }
29 const auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
30 std::fstream macro_file(name, std::ios::out | std::ios::binary);
31 if (!macro_file) {
32 LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
33 Common::FS::PathToUTF8String(name));
34 return;
35 }
36 macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
37}
38
18MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d) 39MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d)
19 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {} 40 : hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {}
20 41
@@ -24,6 +45,11 @@ void MacroEngine::AddCode(u32 method, u32 data) {
24 uploaded_macro_code[method].push_back(data); 45 uploaded_macro_code[method].push_back(data);
25} 46}
26 47
48void MacroEngine::ClearCode(u32 method) {
49 macro_cache.erase(method);
50 uploaded_macro_code.erase(method);
51}
52
27void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) { 53void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
28 auto compiled_macro = macro_cache.find(method); 54 auto compiled_macro = macro_cache.find(method);
29 if (compiled_macro != macro_cache.end()) { 55 if (compiled_macro != macro_cache.end()) {
@@ -45,7 +71,7 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
45 } 71 }
46 } 72 }
47 if (!mid_method.has_value()) { 73 if (!mid_method.has_value()) {
48 UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method); 74 ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
49 return; 75 return;
50 } 76 }
51 } 77 }
@@ -54,6 +80,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
54 if (!mid_method.has_value()) { 80 if (!mid_method.has_value()) {
55 cache_info.lle_program = Compile(macro_code->second); 81 cache_info.lle_program = Compile(macro_code->second);
56 cache_info.hash = boost::hash_value(macro_code->second); 82 cache_info.hash = boost::hash_value(macro_code->second);
83 if (Settings::values.dump_macros) {
84 Dump(cache_info.hash, macro_code->second);
85 }
57 } else { 86 } else {
58 const auto& macro_cached = uploaded_macro_code[mid_method.value()]; 87 const auto& macro_cached = uploaded_macro_code[mid_method.value()];
59 const auto rebased_method = method - mid_method.value(); 88 const auto rebased_method = method - mid_method.value();
@@ -63,6 +92,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
63 code.size() * sizeof(u32)); 92 code.size() * sizeof(u32));
64 cache_info.hash = boost::hash_value(code); 93 cache_info.hash = boost::hash_value(code);
65 cache_info.lle_program = Compile(code); 94 cache_info.lle_program = Compile(code);
95 if (Settings::values.dump_macros) {
96 Dump(cache_info.hash, code);
97 }
66 } 98 }
67 99
68 if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) { 100 if (auto hle_program = hle_macros->GetHLEProgram(cache_info.hash)) {
diff --git a/src/video_core/macro/macro.h b/src/video_core/macro/macro.h
index 7e12c16dc..07d97ba39 100644
--- a/src/video_core/macro/macro.h
+++ b/src/video_core/macro/macro.h
@@ -117,6 +117,9 @@ public:
117 // Store the uploaded macro code to compile them when they're called. 117 // Store the uploaded macro code to compile them when they're called.
118 void AddCode(u32 method, u32 data); 118 void AddCode(u32 method, u32 data);
119 119
120 // Clear the code associated with a method.
121 void ClearCode(u32 method);
122
120 // Compiles the macro if its not in the cache, and executes the compiled macro 123 // Compiles the macro if its not in the cache, and executes the compiled macro
121 void Execute(u32 method, const std::vector<u32>& parameters); 124 void Execute(u32 method, const std::vector<u32>& parameters);
122 125
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index 87d2e8721..f670b1bca 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -308,7 +308,6 @@ bool MacroInterpreterImpl::EvaluateBranchCondition(Macro::BranchCondition cond,
308 return value != 0; 308 return value != 0;
309 } 309 }
310 UNREACHABLE(); 310 UNREACHABLE();
311 return true;
312} 311}
313 312
314Macro::Opcode MacroInterpreterImpl::GetOpcode() const { 313Macro::Opcode MacroInterpreterImpl::GetOpcode() const {
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index dc2b490d4..aca25d902 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -23,7 +23,8 @@ MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255
23namespace Tegra { 23namespace Tegra {
24namespace { 24namespace {
25constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx; 25constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx;
26constexpr Xbyak::Reg32 RESULT = Xbyak::util::ebp; 26constexpr Xbyak::Reg32 RESULT = Xbyak::util::r10d;
27constexpr Xbyak::Reg64 MAX_PARAMETER = Xbyak::util::r11;
27constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12; 28constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
28constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d; 29constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
29constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15; 30constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
@@ -31,6 +32,7 @@ constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
31constexpr std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({ 32constexpr std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
32 STATE, 33 STATE,
33 RESULT, 34 RESULT,
35 MAX_PARAMETER,
34 PARAMETERS, 36 PARAMETERS,
35 METHOD_ADDRESS, 37 METHOD_ADDRESS,
36 BRANCH_HOLDER, 38 BRANCH_HOLDER,
@@ -80,7 +82,7 @@ private:
80 u32 carry_flag{}; 82 u32 carry_flag{};
81 }; 83 };
82 static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0"); 84 static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0");
83 using ProgramType = void (*)(JITState*, const u32*); 85 using ProgramType = void (*)(JITState*, const u32*, const u32*);
84 86
85 struct OptimizerState { 87 struct OptimizerState {
86 bool can_skip_carry{}; 88 bool can_skip_carry{};
@@ -112,7 +114,7 @@ void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) {
112 JITState state{}; 114 JITState state{};
113 state.maxwell3d = &maxwell3d; 115 state.maxwell3d = &maxwell3d;
114 state.registers = {}; 116 state.registers = {};
115 program(&state, parameters.data()); 117 program(&state, parameters.data(), parameters.data() + parameters.size());
116} 118}
117 119
118void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) { 120void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
@@ -409,7 +411,7 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
409 411
410 Xbyak::Label end; 412 Xbyak::Label end;
411 auto value = Compile_GetRegister(opcode.src_a, eax); 413 auto value = Compile_GetRegister(opcode.src_a, eax);
412 test(value, value); 414 cmp(value, 0); // test(value, value);
413 if (optimizer.has_delayed_pc) { 415 if (optimizer.has_delayed_pc) {
414 switch (opcode.branch_condition) { 416 switch (opcode.branch_condition) {
415 case Macro::BranchCondition::Zero: 417 case Macro::BranchCondition::Zero:
@@ -488,6 +490,7 @@ void MacroJITx64Impl::Compile() {
488 // JIT state 490 // JIT state
489 mov(STATE, Common::X64::ABI_PARAM1); 491 mov(STATE, Common::X64::ABI_PARAM1);
490 mov(PARAMETERS, Common::X64::ABI_PARAM2); 492 mov(PARAMETERS, Common::X64::ABI_PARAM2);
493 mov(MAX_PARAMETER, Common::X64::ABI_PARAM3);
491 xor_(RESULT, RESULT); 494 xor_(RESULT, RESULT);
492 xor_(METHOD_ADDRESS, METHOD_ADDRESS); 495 xor_(METHOD_ADDRESS, METHOD_ADDRESS);
493 xor_(BRANCH_HOLDER, BRANCH_HOLDER); 496 xor_(BRANCH_HOLDER, BRANCH_HOLDER);
@@ -598,7 +601,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
598 return true; 601 return true;
599} 602}
600 603
604static void WarnInvalidParameter(uintptr_t parameter, uintptr_t max_parameter) {
605 LOG_CRITICAL(HW_GPU,
606 "Macro JIT: invalid parameter access 0x{:x} (0x{:x} is the last parameter)",
607 parameter, max_parameter - sizeof(u32));
608}
609
601Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() { 610Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() {
611 Xbyak::Label parameter_ok{};
612 cmp(PARAMETERS, MAX_PARAMETER);
613 jb(parameter_ok, T_NEAR);
614 Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
615 mov(Common::X64::ABI_PARAM1, PARAMETERS);
616 mov(Common::X64::ABI_PARAM2, MAX_PARAMETER);
617 Common::X64::CallFarFunction(*this, &WarnInvalidParameter);
618 Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
619 L(parameter_ok);
602 mov(eax, dword[PARAMETERS]); 620 mov(eax, dword[PARAMETERS]);
603 add(PARAMETERS, sizeof(u32)); 621 add(PARAMETERS, sizeof(u32));
604 return eax; 622 return eax;
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index c8d99fdb5..d373be0ba 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -67,7 +67,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
67 ASSERT(it->first == gpu_addr); 67 ASSERT(it->first == gpu_addr);
68 map_ranges.erase(it); 68 map_ranges.erase(it);
69 } else { 69 } else {
70 UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr); 70 ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr);
71 } 71 }
72 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size); 72 const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
73 73
@@ -206,7 +206,7 @@ T MemoryManager::Read(GPUVAddr addr) const {
206 return value; 206 return value;
207 } 207 }
208 208
209 UNREACHABLE(); 209 ASSERT(false);
210 210
211 return {}; 211 return {};
212} 212}
@@ -219,7 +219,7 @@ void MemoryManager::Write(GPUVAddr addr, T data) {
219 return; 219 return;
220 } 220 }
221 221
222 UNREACHABLE(); 222 ASSERT(false);
223} 223}
224 224
225template u8 MemoryManager::Read<u8>(GPUVAddr addr) const; 225template u8 MemoryManager::Read<u8>(GPUVAddr addr) const;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 35f42f2f8..67eae369d 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -48,7 +48,7 @@ GLenum Stage(size_t stage_index) {
48 case 4: 48 case 4:
49 return GL_FRAGMENT_SHADER; 49 return GL_FRAGMENT_SHADER;
50 } 50 }
51 UNREACHABLE_MSG("{}", stage_index); 51 ASSERT_MSG(false, "{}", stage_index);
52 return GL_NONE; 52 return GL_NONE;
53} 53}
54 54
@@ -65,7 +65,7 @@ GLenum AssemblyStage(size_t stage_index) {
65 case 4: 65 case 4:
66 return GL_FRAGMENT_PROGRAM_NV; 66 return GL_FRAGMENT_PROGRAM_NV;
67 } 67 }
68 UNREACHABLE_MSG("{}", stage_index); 68 ASSERT_MSG(false, "{}", stage_index);
69 return GL_NONE; 69 return GL_NONE;
70} 70}
71 71
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8ef79753f..159b71161 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -563,12 +563,11 @@ void RasterizerOpenGL::SyncViewport() {
563 flags[Dirty::FrontFace] = false; 563 flags[Dirty::FrontFace] = false;
564 564
565 GLenum mode = MaxwellToGL::FrontFace(regs.front_face); 565 GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
566 bool flip_faces = false; 566 bool flip_faces = true;
567 if (regs.screen_y_control.triangle_rast_flip != 0 && 567 if (regs.screen_y_control.triangle_rast_flip != 0) {
568 regs.viewport_transform[0].scale_y < 0.0f) {
569 flip_faces = !flip_faces; 568 flip_faces = !flip_faces;
570 } 569 }
571 if (regs.viewport_transform[0].scale_z < 0.0f) { 570 if (regs.viewport_transform[0].scale_y < 0.0f) {
572 flip_faces = !flip_faces; 571 flip_faces = !flip_faces;
573 } 572 }
574 if (flip_faces) { 573 if (flip_faces) {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index cd48fef26..07d4b7cf0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -85,7 +85,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
85 case Maxwell::TessellationPrimitive::Quads: 85 case Maxwell::TessellationPrimitive::Quads:
86 return Shader::TessPrimitive::Quads; 86 return Shader::TessPrimitive::Quads;
87 } 87 }
88 UNREACHABLE(); 88 ASSERT(false);
89 return Shader::TessPrimitive::Triangles; 89 return Shader::TessPrimitive::Triangles;
90 }(); 90 }();
91 info.tess_spacing = [&] { 91 info.tess_spacing = [&] {
@@ -97,7 +97,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
97 case Maxwell::TessellationSpacing::FractionalEven: 97 case Maxwell::TessellationSpacing::FractionalEven:
98 return Shader::TessSpacing::FractionalEven; 98 return Shader::TessSpacing::FractionalEven;
99 } 99 }
100 UNREACHABLE(); 100 ASSERT(false);
101 return Shader::TessSpacing::Equal; 101 return Shader::TessSpacing::Equal;
102 }(); 102 }();
103 break; 103 break;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 29ff736fb..8c0fffc67 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -83,7 +83,7 @@ GLenum ImageTarget(const VideoCommon::ImageInfo& info) {
83 case ImageType::Buffer: 83 case ImageType::Buffer:
84 return GL_TEXTURE_BUFFER; 84 return GL_TEXTURE_BUFFER;
85 } 85 }
86 UNREACHABLE_MSG("Invalid image type={}", info.type); 86 ASSERT_MSG(false, "Invalid image type={}", info.type);
87 return GL_NONE; 87 return GL_NONE;
88} 88}
89 89
@@ -107,7 +107,7 @@ GLenum ImageTarget(Shader::TextureType type, int num_samples = 1) {
107 case Shader::TextureType::Buffer: 107 case Shader::TextureType::Buffer:
108 return GL_TEXTURE_BUFFER; 108 return GL_TEXTURE_BUFFER;
109 } 109 }
110 UNREACHABLE_MSG("Invalid image view type={}", type); 110 ASSERT_MSG(false, "Invalid image view type={}", type);
111 return GL_NONE; 111 return GL_NONE;
112} 112}
113 113
@@ -119,7 +119,7 @@ GLenum TextureMode(PixelFormat format, bool is_first) {
119 case PixelFormat::S8_UINT_D24_UNORM: 119 case PixelFormat::S8_UINT_D24_UNORM:
120 return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; 120 return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
121 default: 121 default:
122 UNREACHABLE(); 122 ASSERT(false);
123 return GL_DEPTH_COMPONENT; 123 return GL_DEPTH_COMPONENT;
124 } 124 }
125} 125}
@@ -140,7 +140,7 @@ GLint Swizzle(SwizzleSource source) {
140 case SwizzleSource::OneFloat: 140 case SwizzleSource::OneFloat:
141 return GL_ONE; 141 return GL_ONE;
142 } 142 }
143 UNREACHABLE_MSG("Invalid swizzle source={}", source); 143 ASSERT_MSG(false, "Invalid swizzle source={}", source);
144 return GL_NONE; 144 return GL_NONE;
145} 145}
146 146
@@ -197,7 +197,7 @@ GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
197 case SwizzleSource::OneFloat: 197 case SwizzleSource::OneFloat:
198 return GL_ONE; 198 return GL_ONE;
199 } 199 }
200 UNREACHABLE_MSG("Invalid swizzle source={}", source); 200 ASSERT_MSG(false, "Invalid swizzle source={}", source);
201 return GL_NONE; 201 return GL_NONE;
202} 202}
203 203
@@ -381,10 +381,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
381 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth); 381 glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth);
382 break; 382 break;
383 case GL_TEXTURE_BUFFER: 383 case GL_TEXTURE_BUFFER:
384 UNREACHABLE(); 384 ASSERT(false);
385 break; 385 break;
386 default: 386 default:
387 UNREACHABLE_MSG("Invalid target=0x{:x}", target); 387 ASSERT_MSG(false, "Invalid target=0x{:x}", target);
388 break; 388 break;
389 } 389 }
390 return texture; 390 return texture;
@@ -420,7 +420,7 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
420 case Shader::ImageFormat::R32G32B32A32_UINT: 420 case Shader::ImageFormat::R32G32B32A32_UINT:
421 return GL_RGBA32UI; 421 return GL_RGBA32UI;
422 } 422 }
423 UNREACHABLE_MSG("Invalid image format={}", format); 423 ASSERT_MSG(false, "Invalid image format={}", format);
424 return GL_R32UI; 424 return GL_R32UI;
425} 425}
426 426
@@ -579,7 +579,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
579 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) { 579 } else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
580 format_conversion_pass.ConvertImage(dst, src, copies); 580 format_conversion_pass.ConvertImage(dst, src, copies);
581 } else { 581 } else {
582 UNREACHABLE(); 582 ASSERT(false);
583 } 583 }
584} 584}
585 585
@@ -620,7 +620,7 @@ void TextureCacheRuntime::AccelerateImageUpload(Image& image, const ImageBufferM
620 case ImageType::Linear: 620 case ImageType::Linear:
621 return util_shaders.PitchUpload(image, map, swizzles); 621 return util_shaders.PitchUpload(image, map, swizzles);
622 default: 622 default:
623 UNREACHABLE(); 623 ASSERT(false);
624 break; 624 break;
625 } 625 }
626} 626}
@@ -639,7 +639,7 @@ FormatProperties TextureCacheRuntime::FormatInfo(ImageType type, GLenum internal
639 case ImageType::e3D: 639 case ImageType::e3D:
640 return format_properties[2].at(internal_format); 640 return format_properties[2].at(internal_format);
641 default: 641 default:
642 UNREACHABLE(); 642 ASSERT(false);
643 return FormatProperties{}; 643 return FormatProperties{};
644 } 644 }
645} 645}
@@ -888,7 +888,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
888 } 888 }
889 break; 889 break;
890 default: 890 default:
891 UNREACHABLE(); 891 ASSERT(false);
892 } 892 }
893} 893}
894 894
@@ -924,7 +924,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
924 depth = copy.image_extent.depth; 924 depth = copy.image_extent.depth;
925 break; 925 break;
926 default: 926 default:
927 UNREACHABLE(); 927 ASSERT(false);
928 } 928 }
929 // Compressed formats don't have a pixel format or type 929 // Compressed formats don't have a pixel format or type
930 const bool is_compressed = gl_format == GL_NONE; 930 const bool is_compressed = gl_format == GL_NONE;
@@ -950,7 +950,7 @@ void Image::Scale(bool up_scale) {
950 case SurfaceType::DepthStencil: 950 case SurfaceType::DepthStencil:
951 return GL_DEPTH_STENCIL_ATTACHMENT; 951 return GL_DEPTH_STENCIL_ATTACHMENT;
952 default: 952 default:
953 UNREACHABLE(); 953 ASSERT(false);
954 return GL_COLOR_ATTACHMENT0; 954 return GL_COLOR_ATTACHMENT0;
955 } 955 }
956 }(); 956 }();
@@ -965,7 +965,7 @@ void Image::Scale(bool up_scale) {
965 case SurfaceType::DepthStencil: 965 case SurfaceType::DepthStencil:
966 return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; 966 return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
967 default: 967 default:
968 UNREACHABLE(); 968 ASSERT(false);
969 return GL_COLOR_BUFFER_BIT; 969 return GL_COLOR_BUFFER_BIT;
970 } 970 }
971 }(); 971 }();
@@ -980,7 +980,7 @@ void Image::Scale(bool up_scale) {
980 case SurfaceType::DepthStencil: 980 case SurfaceType::DepthStencil:
981 return 3; 981 return 3;
982 default: 982 default:
983 UNREACHABLE(); 983 ASSERT(false);
984 return 0; 984 return 0;
985 } 985 }
986 }(); 986 }();
@@ -1045,7 +1045,7 @@ bool Image::ScaleUp(bool ignore) {
1045 return false; 1045 return false;
1046 } 1046 }
1047 if (info.type == ImageType::Linear) { 1047 if (info.type == ImageType::Linear) {
1048 UNREACHABLE(); 1048 ASSERT(false);
1049 return false; 1049 return false;
1050 } 1050 }
1051 flags |= ImageFlagBits::Rescaled; 1051 flags |= ImageFlagBits::Rescaled;
@@ -1139,7 +1139,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1139 UNIMPLEMENTED(); 1139 UNIMPLEMENTED();
1140 break; 1140 break;
1141 case ImageViewType::Buffer: 1141 case ImageViewType::Buffer:
1142 UNREACHABLE(); 1142 ASSERT(false);
1143 break; 1143 break;
1144 } 1144 }
1145 switch (info.type) { 1145 switch (info.type) {
@@ -1319,7 +1319,7 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
1319 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; 1319 buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
1320 break; 1320 break;
1321 default: 1321 default:
1322 UNREACHABLE(); 1322 ASSERT(false);
1323 buffer_bits |= GL_DEPTH_BUFFER_BIT; 1323 buffer_bits |= GL_DEPTH_BUFFER_BIT;
1324 break; 1324 break;
1325 } 1325 }
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index c2a6da5a7..644b60d73 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -206,7 +206,7 @@ inline GLenum IndexFormat(Maxwell::IndexFormat index_format) {
206 case Maxwell::IndexFormat::UnsignedInt: 206 case Maxwell::IndexFormat::UnsignedInt:
207 return GL_UNSIGNED_INT; 207 return GL_UNSIGNED_INT;
208 } 208 }
209 UNREACHABLE_MSG("Invalid index_format={}", index_format); 209 ASSERT_MSG(false, "Invalid index_format={}", index_format);
210 return {}; 210 return {};
211} 211}
212 212
@@ -243,7 +243,7 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
243 case Maxwell::PrimitiveTopology::Patches: 243 case Maxwell::PrimitiveTopology::Patches:
244 return GL_PATCHES; 244 return GL_PATCHES;
245 } 245 }
246 UNREACHABLE_MSG("Invalid topology={}", topology); 246 ASSERT_MSG(false, "Invalid topology={}", topology);
247 return GL_POINTS; 247 return GL_POINTS;
248} 248}
249 249
@@ -271,8 +271,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
271 } 271 }
272 break; 272 break;
273 } 273 }
274 UNREACHABLE_MSG("Invalid texture filter mode={} and mipmap filter mode={}", filter_mode, 274 ASSERT_MSG(false, "Invalid texture filter mode={} and mipmap filter mode={}", filter_mode,
275 mipmap_filter_mode); 275 mipmap_filter_mode);
276 return GL_NEAREST; 276 return GL_NEAREST;
277} 277}
278 278
@@ -550,7 +550,7 @@ inline GLenum PolygonMode(Maxwell::PolygonMode polygon_mode) {
550 case Maxwell::PolygonMode::Fill: 550 case Maxwell::PolygonMode::Fill:
551 return GL_FILL; 551 return GL_FILL;
552 } 552 }
553 UNREACHABLE_MSG("Invalid polygon mode={}", polygon_mode); 553 ASSERT_MSG(false, "Invalid polygon mode={}", polygon_mode);
554 return GL_FILL; 554 return GL_FILL;
555} 555}
556 556
@@ -563,7 +563,7 @@ inline GLenum ReductionFilter(Tegra::Texture::SamplerReduction filter) {
563 case Tegra::Texture::SamplerReduction::Max: 563 case Tegra::Texture::SamplerReduction::Max:
564 return GL_MAX; 564 return GL_MAX;
565 } 565 }
566 UNREACHABLE_MSG("Invalid reduction filter={}", static_cast<int>(filter)); 566 ASSERT_MSG(false, "Invalid reduction filter={}", static_cast<int>(filter));
567 return GL_WEIGHTED_AVERAGE_ARB; 567 return GL_WEIGHTED_AVERAGE_ARB;
568} 568}
569 569
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 3a3c213bb..9a9243544 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -79,7 +79,7 @@ const char* GetSource(GLenum source) {
79 case GL_DEBUG_SOURCE_OTHER: 79 case GL_DEBUG_SOURCE_OTHER:
80 return "OTHER"; 80 return "OTHER";
81 default: 81 default:
82 UNREACHABLE(); 82 ASSERT(false);
83 return "Unknown source"; 83 return "Unknown source";
84 } 84 }
85} 85}
@@ -101,7 +101,7 @@ const char* GetType(GLenum type) {
101 case GL_DEBUG_TYPE_MARKER: 101 case GL_DEBUG_TYPE_MARKER:
102 return "MARKER"; 102 return "MARKER";
103 default: 103 default:
104 UNREACHABLE(); 104 ASSERT(false);
105 return "Unknown type"; 105 return "Unknown type";
106 } 106 }
107} 107}
diff --git a/src/video_core/renderer_opengl/util_shaders.cpp b/src/video_core/renderer_opengl/util_shaders.cpp
index 837825737..404def62e 100644
--- a/src/video_core/renderer_opengl/util_shaders.cpp
+++ b/src/video_core/renderer_opengl/util_shaders.cpp
@@ -282,7 +282,7 @@ GLenum StoreFormat(u32 bytes_per_block) {
282 case 16: 282 case 16:
283 return GL_RGBA32UI; 283 return GL_RGBA32UI;
284 } 284 }
285 UNREACHABLE(); 285 ASSERT(false);
286 return GL_R8UI; 286 return GL_R8UI;
287} 287}
288 288
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index ea360f339..193cbe15e 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -25,7 +25,7 @@ VkFilter Filter(Tegra::Texture::TextureFilter filter) {
25 case Tegra::Texture::TextureFilter::Linear: 25 case Tegra::Texture::TextureFilter::Linear:
26 return VK_FILTER_LINEAR; 26 return VK_FILTER_LINEAR;
27 } 27 }
28 UNREACHABLE_MSG("Invalid sampler filter={}", filter); 28 ASSERT_MSG(false, "Invalid sampler filter={}", filter);
29 return {}; 29 return {};
30} 30}
31 31
@@ -42,7 +42,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
42 case Tegra::Texture::TextureMipmapFilter::Linear: 42 case Tegra::Texture::TextureMipmapFilter::Linear:
43 return VK_SAMPLER_MIPMAP_MODE_LINEAR; 43 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
44 } 44 }
45 UNREACHABLE_MSG("Invalid sampler mipmap mode={}", mipmap_filter); 45 ASSERT_MSG(false, "Invalid sampler mipmap mode={}", mipmap_filter);
46 return {}; 46 return {};
47} 47}
48 48
@@ -70,7 +70,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
70 case Tegra::Texture::TextureFilter::Linear: 70 case Tegra::Texture::TextureFilter::Linear:
71 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; 71 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
72 } 72 }
73 UNREACHABLE(); 73 ASSERT(false);
74 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 74 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
75 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: 75 case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
76 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; 76 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
@@ -744,7 +744,7 @@ VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle)
744 case Maxwell::ViewportSwizzle::NegativeW: 744 case Maxwell::ViewportSwizzle::NegativeW:
745 return VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV; 745 return VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV;
746 } 746 }
747 UNREACHABLE_MSG("Invalid swizzle={}", swizzle); 747 ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
748 return {}; 748 return {};
749} 749}
750 750
@@ -757,7 +757,7 @@ VkSamplerReductionMode SamplerReduction(Tegra::Texture::SamplerReduction reducti
757 case Tegra::Texture::SamplerReduction::Max: 757 case Tegra::Texture::SamplerReduction::Max:
758 return VK_SAMPLER_REDUCTION_MODE_MAX_EXT; 758 return VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
759 } 759 }
760 UNREACHABLE_MSG("Invalid sampler mode={}", static_cast<int>(reduction)); 760 ASSERT_MSG(false, "Invalid sampler mode={}", static_cast<int>(reduction));
761 return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; 761 return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
762} 762}
763 763
@@ -780,7 +780,7 @@ VkSampleCountFlagBits MsaaMode(Tegra::Texture::MsaaMode msaa_mode) {
780 case Tegra::Texture::MsaaMode::Msaa4x4: 780 case Tegra::Texture::MsaaMode::Msaa4x4:
781 return VK_SAMPLE_COUNT_16_BIT; 781 return VK_SAMPLE_COUNT_16_BIT;
782 default: 782 default:
783 UNREACHABLE_MSG("Invalid msaa_mode={}", static_cast<int>(msaa_mode)); 783 ASSERT_MSG(false, "Invalid msaa_mode={}", static_cast<int>(msaa_mode));
784 return VK_SAMPLE_COUNT_1_BIT; 784 return VK_SAMPLE_COUNT_1_BIT;
785 } 785 }
786} 786}
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 0aeb37538..450905197 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -46,7 +46,7 @@ size_t BytesPerIndex(VkIndexType index_type) {
46 case VK_INDEX_TYPE_UINT32: 46 case VK_INDEX_TYPE_UINT32:
47 return 4; 47 return 4;
48 default: 48 default:
49 UNREACHABLE_MSG("Invalid index type={}", index_type); 49 ASSERT_MSG(false, "Invalid index type={}", index_type);
50 return 1; 50 return 1;
51 } 51 }
52} 52}
@@ -366,7 +366,7 @@ void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle
366 std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size); 366 std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size);
367 break; 367 break;
368 default: 368 default:
369 UNREACHABLE(); 369 ASSERT(false);
370 break; 370 break;
371 } 371 }
372 staging_data += quad_size; 372 staging_data += quad_size;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 29481a102..4cba777e6 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -265,7 +265,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
265 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt: 265 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt:
266 return 2; 266 return 2;
267 } 267 }
268 UNREACHABLE(); 268 ASSERT(false);
269 return 2; 269 return 2;
270 }(); 270 }();
271 const u32 input_size = num_vertices << index_shift; 271 const u32 input_size = num_vertices << index_shift;
@@ -328,31 +328,32 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
328 const VkImageAspectFlags aspect_mask = image.AspectMask(); 328 const VkImageAspectFlags aspect_mask = image.AspectMask();
329 const VkImage vk_image = image.Handle(); 329 const VkImage vk_image = image.Handle();
330 const bool is_initialized = image.ExchangeInitialization(); 330 const bool is_initialized = image.ExchangeInitialization();
331 scheduler.Record( 331 scheduler.Record([vk_pipeline, vk_image, aspect_mask,
332 [vk_pipeline, vk_image, aspect_mask, is_initialized](vk::CommandBuffer cmdbuf) { 332 is_initialized](vk::CommandBuffer cmdbuf) {
333 const VkImageMemoryBarrier image_barrier{ 333 const VkImageMemoryBarrier image_barrier{
334 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 334 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
335 .pNext = nullptr, 335 .pNext = nullptr,
336 .srcAccessMask = is_initialized ? VK_ACCESS_SHADER_WRITE_BIT : VkAccessFlags{}, 336 .srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
337 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 337 : VK_ACCESS_NONE),
338 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, 338 .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
339 .newLayout = VK_IMAGE_LAYOUT_GENERAL, 339 .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
340 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 340 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
341 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, 341 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
342 .image = vk_image, 342 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
343 .subresourceRange{ 343 .image = vk_image,
344 .aspectMask = aspect_mask, 344 .subresourceRange{
345 .baseMipLevel = 0, 345 .aspectMask = aspect_mask,
346 .levelCount = VK_REMAINING_MIP_LEVELS, 346 .baseMipLevel = 0,
347 .baseArrayLayer = 0, 347 .levelCount = VK_REMAINING_MIP_LEVELS,
348 .layerCount = VK_REMAINING_ARRAY_LAYERS, 348 .baseArrayLayer = 0,
349 }, 349 .layerCount = VK_REMAINING_ARRAY_LAYERS,
350 }; 350 },
351 cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT 351 };
352 : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 352 cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT
353 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier); 353 : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
354 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline); 354 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
355 }); 355 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
356 });
356 for (const VideoCommon::SwizzleParameters& swizzle : swizzles) { 357 for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
357 const size_t input_offset = swizzle.buffer_offset + map.offset; 358 const size_t input_offset = swizzle.buffer_offset + map.offset;
358 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U); 359 const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 8U);
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 5196bdcf2..978e827f5 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -174,7 +174,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
174 case Maxwell::TessellationPrimitive::Quads: 174 case Maxwell::TessellationPrimitive::Quads:
175 return Shader::TessPrimitive::Quads; 175 return Shader::TessPrimitive::Quads;
176 } 176 }
177 UNREACHABLE(); 177 ASSERT(false);
178 return Shader::TessPrimitive::Triangles; 178 return Shader::TessPrimitive::Triangles;
179 }(); 179 }();
180 info.tess_spacing = [&] { 180 info.tess_spacing = [&] {
@@ -187,7 +187,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
187 case Maxwell::TessellationSpacing::FractionalEven: 187 case Maxwell::TessellationSpacing::FractionalEven:
188 return Shader::TessSpacing::FractionalEven; 188 return Shader::TessSpacing::FractionalEven;
189 } 189 }
190 UNREACHABLE(); 190 ASSERT(false);
191 return Shader::TessSpacing::Equal; 191 return Shader::TessSpacing::Equal;
192 }(); 192 }();
193 break; 193 break;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index fd27581ce..ce6c853c1 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -784,8 +784,8 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
784 }); 784 });
785 } else { 785 } else {
786 // Front face defines both faces 786 // Front face defines both faces
787 scheduler.Record([ref = regs.stencil_back_func_ref, write_mask = regs.stencil_back_mask, 787 scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
788 test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) { 788 test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
789 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref); 789 cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
790 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask); 790 cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
791 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask); 791 cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_AND_BACK, test_mask);
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 31ce2f815..9a6afaca6 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -263,7 +263,7 @@ StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage
263 case MemoryUsage::Download: 263 case MemoryUsage::Download:
264 return download_cache; 264 return download_cache;
265 default: 265 default:
266 UNREACHABLE_MSG("Invalid memory usage={}", usage); 266 ASSERT_MSG(false, "Invalid memory usage={}", usage);
267 return upload_cache; 267 return upload_cache;
268 } 268 }
269} 269}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 353594293..43ecb9647 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -70,7 +70,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
70 case ImageType::Buffer: 70 case ImageType::Buffer:
71 break; 71 break;
72 } 72 }
73 UNREACHABLE_MSG("Invalid image type={}", type); 73 ASSERT_MSG(false, "Invalid image type={}", type);
74 return {}; 74 return {};
75} 75}
76 76
@@ -87,7 +87,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
87 case 16: 87 case 16:
88 return VK_SAMPLE_COUNT_16_BIT; 88 return VK_SAMPLE_COUNT_16_BIT;
89 default: 89 default:
90 UNREACHABLE_MSG("Invalid number of samples={}", num_samples); 90 ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
91 return VK_SAMPLE_COUNT_1_BIT; 91 return VK_SAMPLE_COUNT_1_BIT;
92 } 92 }
93} 93}
@@ -107,7 +107,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
107 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; 107 usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
108 break; 108 break;
109 default: 109 default:
110 UNREACHABLE_MSG("Invalid surface type"); 110 ASSERT_MSG(false, "Invalid surface type");
111 } 111 }
112 } 112 }
113 if (info.storage) { 113 if (info.storage) {
@@ -179,7 +179,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
179 case VideoCore::Surface::SurfaceType::DepthStencil: 179 case VideoCore::Surface::SurfaceType::DepthStencil:
180 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; 180 return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
181 default: 181 default:
182 UNREACHABLE_MSG("Invalid surface type"); 182 ASSERT_MSG(false, "Invalid surface type");
183 return VkImageAspectFlags{}; 183 return VkImageAspectFlags{};
184 } 184 }
185} 185}
@@ -221,7 +221,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
221 case SwizzleSource::OneInt: 221 case SwizzleSource::OneInt:
222 return VK_COMPONENT_SWIZZLE_ONE; 222 return VK_COMPONENT_SWIZZLE_ONE;
223 } 223 }
224 UNREACHABLE_MSG("Invalid swizzle={}", swizzle); 224 ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
225 return VK_COMPONENT_SWIZZLE_ZERO; 225 return VK_COMPONENT_SWIZZLE_ZERO;
226} 226}
227 227
@@ -242,10 +242,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
242 case Shader::TextureType::ColorArrayCube: 242 case Shader::TextureType::ColorArrayCube:
243 return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; 243 return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
244 case Shader::TextureType::Buffer: 244 case Shader::TextureType::Buffer:
245 UNREACHABLE_MSG("Texture buffers can't be image views"); 245 ASSERT_MSG(false, "Texture buffers can't be image views");
246 return VK_IMAGE_VIEW_TYPE_1D; 246 return VK_IMAGE_VIEW_TYPE_1D;
247 } 247 }
248 UNREACHABLE_MSG("Invalid image view type={}", type); 248 ASSERT_MSG(false, "Invalid image view type={}", type);
249 return VK_IMAGE_VIEW_TYPE_2D; 249 return VK_IMAGE_VIEW_TYPE_2D;
250} 250}
251 251
@@ -269,10 +269,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
269 UNIMPLEMENTED_MSG("Rect image view"); 269 UNIMPLEMENTED_MSG("Rect image view");
270 return VK_IMAGE_VIEW_TYPE_2D; 270 return VK_IMAGE_VIEW_TYPE_2D;
271 case VideoCommon::ImageViewType::Buffer: 271 case VideoCommon::ImageViewType::Buffer:
272 UNREACHABLE_MSG("Texture buffers can't be image views"); 272 ASSERT_MSG(false, "Texture buffers can't be image views");
273 return VK_IMAGE_VIEW_TYPE_1D; 273 return VK_IMAGE_VIEW_TYPE_1D;
274 } 274 }
275 UNREACHABLE_MSG("Invalid image view type={}", type); 275 ASSERT_MSG(false, "Invalid image view type={}", type);
276 return VK_IMAGE_VIEW_TYPE_2D; 276 return VK_IMAGE_VIEW_TYPE_2D;
277} 277}
278 278
@@ -644,7 +644,7 @@ struct RangedBarrierRange {
644 case Shader::ImageFormat::R32G32B32A32_UINT: 644 case Shader::ImageFormat::R32G32B32A32_UINT:
645 return VK_FORMAT_R32G32B32A32_UINT; 645 return VK_FORMAT_R32G32B32A32_UINT;
646 } 646 }
647 UNREACHABLE_MSG("Invalid image format={}", format); 647 ASSERT_MSG(false, "Invalid image format={}", format);
648 return VK_FORMAT_R32_UINT; 648 return VK_FORMAT_R32_UINT;
649} 649}
650 650
@@ -1596,7 +1596,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1596 UNIMPLEMENTED(); 1596 UNIMPLEMENTED();
1597 break; 1597 break;
1598 case VideoCommon::ImageViewType::Buffer: 1598 case VideoCommon::ImageViewType::Buffer:
1599 UNREACHABLE(); 1599 ASSERT(false);
1600 break; 1600 break;
1601 } 1601 }
1602} 1602}
@@ -1822,7 +1822,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
1822 if (IsPixelFormatASTC(image.info.format)) { 1822 if (IsPixelFormatASTC(image.info.format)) {
1823 return astc_decoder_pass.Assemble(image, map, swizzles); 1823 return astc_decoder_pass.Assemble(image, map, swizzles);
1824 } 1824 }
1825 UNREACHABLE(); 1825 ASSERT(false);
1826} 1826}
1827 1827
1828} // namespace Vulkan 1828} // namespace Vulkan
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index d469964f6..c4e923bbf 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -280,7 +280,7 @@ GraphicsEnvironment::GraphicsEnvironment(Tegra::Engines::Maxwell3D& maxwell3d_,
280 stage_index = 4; 280 stage_index = 4;
281 break; 281 break;
282 default: 282 default:
283 UNREACHABLE_MSG("Invalid program={}", program); 283 ASSERT_MSG(false, "Invalid program={}", program);
284 break; 284 break;
285 } 285 }
286 const u64 local_size{sph.LocalMemorySize()}; 286 const u64 local_size{sph.LocalMemorySize()};
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 5f428d35d..69c1b1e6d 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -29,7 +29,7 @@ SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_t
29 return SurfaceTarget::Texture2DArray; 29 return SurfaceTarget::Texture2DArray;
30 default: 30 default:
31 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", texture_type); 31 LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", texture_type);
32 UNREACHABLE(); 32 ASSERT(false);
33 return SurfaceTarget::Texture2D; 33 return SurfaceTarget::Texture2D;
34 } 34 }
35} 35}
@@ -48,7 +48,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
48 return true; 48 return true;
49 default: 49 default:
50 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target); 50 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
51 UNREACHABLE(); 51 ASSERT(false);
52 return false; 52 return false;
53 } 53 }
54} 54}
@@ -67,7 +67,7 @@ bool SurfaceTargetIsArray(SurfaceTarget target) {
67 return true; 67 return true;
68 default: 68 default:
69 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target); 69 LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
70 UNREACHABLE(); 70 ASSERT(false);
71 return false; 71 return false;
72 } 72 }
73} 73}
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 86fea61ae..75e055592 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -147,7 +147,7 @@ enum class SurfaceTarget {
147 TextureCubeArray, 147 TextureCubeArray,
148}; 148};
149 149
150constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 150constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
151 1, // A8B8G8R8_UNORM 151 1, // A8B8G8R8_UNORM
152 1, // A8B8G8R8_SNORM 152 1, // A8B8G8R8_SNORM
153 1, // A8B8G8R8_SINT 153 1, // A8B8G8R8_SINT
@@ -249,7 +249,7 @@ constexpr u32 DefaultBlockWidth(PixelFormat format) {
249 return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)]; 249 return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
250} 250}
251 251
252constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 252constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
253 1, // A8B8G8R8_UNORM 253 1, // A8B8G8R8_UNORM
254 1, // A8B8G8R8_SNORM 254 1, // A8B8G8R8_SNORM
255 1, // A8B8G8R8_SINT 255 1, // A8B8G8R8_SINT
@@ -351,7 +351,7 @@ constexpr u32 DefaultBlockHeight(PixelFormat format) {
351 return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)]; 351 return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
352} 352}
353 353
354constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 354constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
355 32, // A8B8G8R8_UNORM 355 32, // A8B8G8R8_UNORM
356 32, // A8B8G8R8_SNORM 356 32, // A8B8G8R8_SNORM
357 32, // A8B8G8R8_SINT 357 32, // A8B8G8R8_SINT
diff --git a/src/video_core/texture_cache/image_info.cpp b/src/video_core/texture_cache/image_info.cpp
index 802939f6c..6c073ee57 100644
--- a/src/video_core/texture_cache/image_info.cpp
+++ b/src/video_core/texture_cache/image_info.cpp
@@ -94,7 +94,7 @@ ImageInfo::ImageInfo(const TICEntry& config) noexcept {
94 resources.layers = 1; 94 resources.layers = 1;
95 break; 95 break;
96 default: 96 default:
97 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 97 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
98 break; 98 break;
99 } 99 }
100 if (type != ImageType::Linear) { 100 if (type != ImageType::Linear) {
diff --git a/src/video_core/texture_cache/image_view_info.cpp b/src/video_core/texture_cache/image_view_info.cpp
index 0cee5e45f..f47885147 100644
--- a/src/video_core/texture_cache/image_view_info.cpp
+++ b/src/video_core/texture_cache/image_view_info.cpp
@@ -71,7 +71,7 @@ ImageViewInfo::ImageViewInfo(const TICEntry& config, s32 base_layer) noexcept
71 range.extent.layers = config.Depth() * 6; 71 range.extent.layers = config.Depth() * 6;
72 break; 72 break;
73 default: 73 default:
74 UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value())); 74 ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
75 break; 75 break;
76 } 76 }
77} 77}
diff --git a/src/video_core/texture_cache/samples_helper.h b/src/video_core/texture_cache/samples_helper.h
index 91fec60bd..d552bccf0 100644
--- a/src/video_core/texture_cache/samples_helper.h
+++ b/src/video_core/texture_cache/samples_helper.h
@@ -23,7 +23,7 @@ namespace VideoCommon {
23 case 16: 23 case 16:
24 return {2, 2}; 24 return {2, 2};
25 } 25 }
26 UNREACHABLE_MSG("Invalid number of samples={}", num_samples); 26 ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
27 return {1, 1}; 27 return {1, 1};
28} 28}
29 29
@@ -47,7 +47,7 @@ namespace VideoCommon {
47 case MsaaMode::Msaa4x4: 47 case MsaaMode::Msaa4x4:
48 return 16; 48 return 16;
49 } 49 }
50 UNREACHABLE_MSG("Invalid MSAA mode={}", static_cast<int>(msaa_mode)); 50 ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
51 return 1; 51 return 1;
52} 52}
53 53
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 6622d7818..cf3ca06a6 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -1485,14 +1485,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) { 1485 std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
1486 const auto page_it = selected_page_table.find(page); 1486 const auto page_it = selected_page_table.find(page);
1487 if (page_it == selected_page_table.end()) { 1487 if (page_it == selected_page_table.end()) {
1488 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1488 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1489 return; 1489 return;
1490 } 1490 }
1491 std::vector<ImageId>& image_ids = page_it->second; 1491 std::vector<ImageId>& image_ids = page_it->second;
1492 const auto vector_it = std::ranges::find(image_ids, image_id); 1492 const auto vector_it = std::ranges::find(image_ids, image_id);
1493 if (vector_it == image_ids.end()) { 1493 if (vector_it == image_ids.end()) {
1494 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", 1494 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1495 page << PAGE_BITS); 1495 page << PAGE_BITS);
1496 return; 1496 return;
1497 } 1497 }
1498 image_ids.erase(vector_it); 1498 image_ids.erase(vector_it);
@@ -1504,14 +1504,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) { 1504 ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
1505 const auto page_it = page_table.find(page); 1505 const auto page_it = page_table.find(page);
1506 if (page_it == page_table.end()) { 1506 if (page_it == page_table.end()) {
1507 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1507 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1508 return; 1508 return;
1509 } 1509 }
1510 std::vector<ImageMapId>& image_map_ids = page_it->second; 1510 std::vector<ImageMapId>& image_map_ids = page_it->second;
1511 const auto vector_it = std::ranges::find(image_map_ids, map_id); 1511 const auto vector_it = std::ranges::find(image_map_ids, map_id);
1512 if (vector_it == image_map_ids.end()) { 1512 if (vector_it == image_map_ids.end()) {
1513 UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}", 1513 ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
1514 page << PAGE_BITS); 1514 page << PAGE_BITS);
1515 return; 1515 return;
1516 } 1516 }
1517 image_map_ids.erase(vector_it); 1517 image_map_ids.erase(vector_it);
@@ -1532,7 +1532,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) { 1532 ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
1533 const auto page_it = page_table.find(page); 1533 const auto page_it = page_table.find(page);
1534 if (page_it == page_table.end()) { 1534 if (page_it == page_table.end()) {
1535 UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS); 1535 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
1536 return; 1536 return;
1537 } 1537 }
1538 std::vector<ImageMapId>& image_map_ids = page_it->second; 1538 std::vector<ImageMapId>& image_map_ids = page_it->second;
@@ -1616,15 +1616,15 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
1616 const GPUVAddr gpu_addr = image.gpu_addr; 1616 const GPUVAddr gpu_addr = image.gpu_addr;
1617 const auto alloc_it = image_allocs_table.find(gpu_addr); 1617 const auto alloc_it = image_allocs_table.find(gpu_addr);
1618 if (alloc_it == image_allocs_table.end()) { 1618 if (alloc_it == image_allocs_table.end()) {
1619 UNREACHABLE_MSG("Trying to delete an image alloc that does not exist in address 0x{:x}", 1619 ASSERT_MSG(false, "Trying to delete an image alloc that does not exist in address 0x{:x}",
1620 gpu_addr); 1620 gpu_addr);
1621 return; 1621 return;
1622 } 1622 }
1623 const ImageAllocId alloc_id = alloc_it->second; 1623 const ImageAllocId alloc_id = alloc_it->second;
1624 std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images; 1624 std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images;
1625 const auto alloc_image_it = std::ranges::find(alloc_images, image_id); 1625 const auto alloc_image_it = std::ranges::find(alloc_images, image_id);
1626 if (alloc_image_it == alloc_images.end()) { 1626 if (alloc_image_it == alloc_images.end()) {
1627 UNREACHABLE_MSG("Trying to delete an image that does not exist"); 1627 ASSERT_MSG(false, "Trying to delete an image that does not exist");
1628 return; 1628 return;
1629 } 1629 }
1630 ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked"); 1630 ASSERT_MSG(False(image.flags & ImageFlagBits::Tracked), "Image was not untracked");
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index c81343850..9b6b8527b 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -87,7 +87,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
87 BPP_CASE(16) 87 BPP_CASE(16)
88#undef BPP_CASE 88#undef BPP_CASE
89 default: 89 default:
90 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 90 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
91 } 91 }
92} 92}
93 93
@@ -209,7 +209,7 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
209 BPP_CASE(16) 209 BPP_CASE(16)
210#undef BPP_CASE 210#undef BPP_CASE
211 default: 211 default:
212 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 212 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
213 } 213 }
214} 214}
215 215
@@ -230,7 +230,7 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
230 BPP_CASE(16) 230 BPP_CASE(16)
231#undef BPP_CASE 231#undef BPP_CASE
232 default: 232 default:
233 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 233 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
234 } 234 }
235} 235}
236 236
@@ -253,7 +253,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
253 BPP_CASE(16) 253 BPP_CASE(16)
254#undef BPP_CASE 254#undef BPP_CASE
255 default: 255 default:
256 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 256 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
257 } 257 }
258} 258}
259 259
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 7b2ca8046..11ce865a7 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -566,7 +566,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
566 } 566 }
567 567
568 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout; 568 VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout;
569 if (khr_workgroup_memory_explicit_layout) { 569 if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) {
570 workgroup_layout = { 570 workgroup_layout = {
571 .sType = 571 .sType =
572 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR, 572 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
@@ -577,6 +577,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
577 .workgroupMemoryExplicitLayout16BitAccess = VK_TRUE, 577 .workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
578 }; 578 };
579 SetNext(next, workgroup_layout); 579 SetNext(next, workgroup_layout);
580 } else if (khr_workgroup_memory_explicit_layout) {
581 // TODO(lat9nq): Find a proper fix for this
582 LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
583 "yuzu bug when host driver does not support 16-bit integers");
584 khr_workgroup_memory_explicit_layout = false;
580 } 585 }
581 586
582 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties; 587 VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
@@ -664,6 +669,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
664 const bool is_amd = 669 const bool is_amd =
665 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; 670 driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
666 if (is_amd) { 671 if (is_amd) {
672 // TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor
673 const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 &&
674 VK_VERSION_MINOR(properties.driverVersion) == 0 &&
675 VK_VERSION_PATCH(properties.driverVersion) >= 226;
676 if (khr_push_descriptor && has_broken_push_descriptor) {
677 LOG_WARNING(
678 Render_Vulkan,
679 "Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor");
680 khr_push_descriptor = false;
681 }
682
667 // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2. 683 // AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
668 sets_per_pool = 96; 684 sets_per_pool = 96;
669 // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken. 685 // Disable VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT on AMD GCN4 and lower as it is broken.
@@ -722,9 +738,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
722 // The wanted format is not supported by hardware, search for alternatives 738 // The wanted format is not supported by hardware, search for alternatives
723 const VkFormat* alternatives = GetFormatAlternatives(wanted_format); 739 const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
724 if (alternatives == nullptr) { 740 if (alternatives == nullptr) {
725 UNREACHABLE_MSG("Format={} with usage={} and type={} has no defined alternatives and host " 741 ASSERT_MSG(false,
726 "hardware does not support it", 742 "Format={} with usage={} and type={} has no defined alternatives and host "
727 wanted_format, wanted_usage, format_type); 743 "hardware does not support it",
744 wanted_format, wanted_usage, format_type);
728 return wanted_format; 745 return wanted_format;
729 } 746 }
730 747
@@ -740,9 +757,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
740 } 757 }
741 758
742 // No alternatives found, panic 759 // No alternatives found, panic
743 UNREACHABLE_MSG("Format={} with usage={} and type={} is not supported by the host hardware and " 760 ASSERT_MSG(false,
744 "doesn't support any of the alternatives", 761 "Format={} with usage={} and type={} is not supported by the host hardware and "
745 wanted_format, wanted_usage, format_type); 762 "doesn't support any of the alternatives",
763 wanted_format, wanted_usage, format_type);
746 return wanted_format; 764 return wanted_format;
747} 765}
748 766
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp
index a5dd33fb2..4eb3913ee 100644
--- a/src/video_core/vulkan_common/vulkan_library.cpp
+++ b/src/video_core/vulkan_common/vulkan_library.cpp
@@ -5,11 +5,13 @@
5 5
6#include "common/dynamic_library.h" 6#include "common/dynamic_library.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_library.h" 9#include "video_core/vulkan_common/vulkan_library.h"
9 10
10namespace Vulkan { 11namespace Vulkan {
11 12
12Common::DynamicLibrary OpenLibrary() { 13Common::DynamicLibrary OpenLibrary() {
14 LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
13 Common::DynamicLibrary library; 15 Common::DynamicLibrary library;
14#ifdef __APPLE__ 16#ifdef __APPLE__
15 // Check if a path to a specific Vulkan library has been specified. 17 // Check if a path to a specific Vulkan library has been specified.
@@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() {
22 } 24 }
23#else 25#else
24 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); 26 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
27 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
25 if (!library.Open(filename.c_str())) { 28 if (!library.Open(filename.c_str())) {
26 // Android devices may not have libvulkan.so.1, only libvulkan.so. 29 // Android devices may not have libvulkan.so.1, only libvulkan.so.
27 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); 30 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
31 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
28 void(library.Open(filename.c_str())); 32 void(library.Open(filename.c_str()));
29 } 33 }
30#endif 34#endif
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index caae6dfdc..6442898bd 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -49,7 +49,7 @@ struct Range {
49 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | 49 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
50 VK_MEMORY_PROPERTY_HOST_CACHED_BIT; 50 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
51 } 51 }
52 UNREACHABLE_MSG("Invalid memory usage={}", usage); 52 ASSERT_MSG(false, "Invalid memory usage={}", usage);
53 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 53 return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
54} 54}
55 55
@@ -325,7 +325,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
325 // Remove device local, if it's not supported by the requested resource 325 // Remove device local, if it's not supported by the requested resource
326 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); 326 return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
327 } 327 }
328 UNREACHABLE_MSG("No compatible memory types found"); 328 ASSERT_MSG(false, "No compatible memory types found");
329 return 0; 329 return 0;
330} 330}
331 331
@@ -349,7 +349,7 @@ bool IsHostVisible(MemoryUsage usage) noexcept {
349 case MemoryUsage::Download: 349 case MemoryUsage::Download:
350 return true; 350 return true;
351 } 351 }
352 UNREACHABLE_MSG("Invalid memory usage={}", usage); 352 ASSERT_MSG(false, "Invalid memory usage={}", usage);
353 return false; 353 return false;
354} 354}
355 355
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index b1ea6075a..2ad98dcfe 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -325,6 +325,8 @@ const char* ToString(VkResult result) noexcept {
325 return "VK_PIPELINE_COMPILE_REQUIRED_EXT"; 325 return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
326 case VkResult::VK_RESULT_MAX_ENUM: 326 case VkResult::VK_RESULT_MAX_ENUM:
327 return "VK_RESULT_MAX_ENUM"; 327 return "VK_RESULT_MAX_ENUM";
328 case VkResult::VK_ERROR_COMPRESSION_EXHAUSTED_EXT:
329 return "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
328 } 330 }
329 return "Unknown"; 331 return "Unknown";
330} 332}
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 6215c914f..46faddb61 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -13,8 +13,8 @@ namespace WebService {
13namespace Telemetry = Common::Telemetry; 13namespace Telemetry = Common::Telemetry;
14 14
15struct TelemetryJson::Impl { 15struct TelemetryJson::Impl {
16 Impl(std::string host, std::string username, std::string token) 16 Impl(std::string host_, std::string username_, std::string token_)
17 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {} 17 : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {}
18 18
19 nlohmann::json& TopSection() { 19 nlohmann::json& TopSection() {
20 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 20 return sections[static_cast<u8>(Telemetry::FieldType::None)];
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 58b0c2f10..dce9772fe 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -30,10 +30,10 @@ constexpr std::array<const char, 1> API_VERSION{'1'};
30constexpr std::size_t TIMEOUT_SECONDS = 30; 30constexpr std::size_t TIMEOUT_SECONDS = 30;
31 31
32struct Client::Impl { 32struct Client::Impl {
33 Impl(std::string host, std::string username, std::string token) 33 Impl(std::string host_, std::string username_, std::string token_)
34 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} { 34 : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {
35 std::scoped_lock lock{jwt_cache.mutex}; 35 std::scoped_lock lock{jwt_cache.mutex};
36 if (this->username == jwt_cache.username && this->token == jwt_cache.token) { 36 if (username == jwt_cache.username && token == jwt_cache.token) {
37 jwt = jwt_cache.jwt; 37 jwt = jwt_cache.jwt;
38 } 38 }
39 } 39 }
@@ -69,8 +69,8 @@ struct Client::Impl {
69 */ 69 */
70 WebResult GenericRequest(const std::string& method, const std::string& path, 70 WebResult GenericRequest(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& accept, 71 const std::string& data, const std::string& accept,
72 const std::string& jwt = "", const std::string& username = "", 72 const std::string& jwt_ = "", const std::string& username_ = "",
73 const std::string& token = "") { 73 const std::string& token_ = "") {
74 if (cli == nullptr) { 74 if (cli == nullptr) {
75 cli = std::make_unique<httplib::Client>(host.c_str()); 75 cli = std::make_unique<httplib::Client>(host.c_str());
76 } 76 }
@@ -85,14 +85,14 @@ struct Client::Impl {
85 cli->set_write_timeout(TIMEOUT_SECONDS); 85 cli->set_write_timeout(TIMEOUT_SECONDS);
86 86
87 httplib::Headers params; 87 httplib::Headers params;
88 if (!jwt.empty()) { 88 if (!jwt_.empty()) {
89 params = { 89 params = {
90 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 90 {std::string("Authorization"), fmt::format("Bearer {}", jwt_)},
91 }; 91 };
92 } else if (!username.empty()) { 92 } else if (!username_.empty()) {
93 params = { 93 params = {
94 {std::string("x-username"), username}, 94 {std::string("x-username"), username_},
95 {std::string("x-token"), token}, 95 {std::string("x-token"), token_},
96 }; 96 };
97 } 97 }
98 98
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 2ee21f751..242867a4f 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,6 +30,8 @@ add_executable(yuzu
30 applets/qt_web_browser_scripts.h 30 applets/qt_web_browser_scripts.h
31 bootmanager.cpp 31 bootmanager.cpp
32 bootmanager.h 32 bootmanager.h
33 check_vulkan.cpp
34 check_vulkan.h
33 compatdb.ui 35 compatdb.ui
34 compatibility_list.cpp 36 compatibility_list.cpp
35 compatibility_list.h 37 compatibility_list.h
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
187 # Update source TS file if enabled 189 # Update source TS file if enabled
188 if (GENERATE_QT_TRANSLATION) 190 if (GENERATE_QT_TRANSLATION)
189 get_target_property(SRCS yuzu SOURCES) 191 get_target_property(SRCS yuzu SOURCES)
190 qt5_create_translation(QM_FILES 192 qt_create_translation(QM_FILES
191 ${SRCS} 193 ${SRCS}
192 ${UIS} 194 ${UIS}
193 ${YUZU_QT_LANGUAGES}/en.ts 195 ${YUZU_QT_LANGUAGES}/en.ts
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
203 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) 205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
204 206
205 # Compile TS files to QM files 207 # Compile TS files to QM files
206 qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 208 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
207 209
208 # Build a QRC file from the QM file list 210 # Build a QRC file from the QM file list
209 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
215 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") 217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
216 218
217 # Add the QRC file to package in all QM files 219 # Add the QRC file to package in all QM files
218 qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) 220 qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
219else() 221else()
220 set(LANGUAGES) 222 set(LANGUAGES)
221endif() 223endif()
@@ -236,18 +238,23 @@ if (APPLE)
236 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 238 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
237elseif(WIN32) 239elseif(WIN32)
238 # compile as a win32 gui application instead of a console application 240 # compile as a win32 gui application instead of a console application
239 target_link_libraries(yuzu PRIVATE Qt5::WinMain) 241 if (QT_VERSION VERSION_GREATER 6)
242 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
243 else()
244 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
245 endif()
240 if(MSVC) 246 if(MSVC)
247 target_link_libraries(yuzu PRIVATE version.lib)
241 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") 248 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
242 elseif(MINGW) 249 elseif(MINGW)
243 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows") 250 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
244 endif() 251 endif()
245endif() 252endif()
246 253
247create_target_directory_groups(yuzu) 254create_target_directory_groups(yuzu)
248 255
249target_link_libraries(yuzu PRIVATE common core input_common video_core) 256target_link_libraries(yuzu PRIVATE common core input_common video_core)
250target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) 257target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
251target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 258target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
252 259
253target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 260target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -255,7 +262,7 @@ if (NOT WIN32)
255 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 262 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
256endif() 263endif()
257if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 264if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
258 target_link_libraries(yuzu PRIVATE Qt5::DBus) 265 target_link_libraries(yuzu PRIVATE Qt::DBus)
259endif() 266endif()
260 267
261target_compile_definitions(yuzu PRIVATE 268target_compile_definitions(yuzu PRIVATE
@@ -291,7 +298,7 @@ if (USE_DISCORD_PRESENCE)
291endif() 298endif()
292 299
293if (YUZU_USE_QT_WEB_ENGINE) 300if (YUZU_USE_QT_WEB_ENGINE)
294 target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) 301 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
295 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 302 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
296endif () 303endif ()
297 304
@@ -319,3 +326,7 @@ endif()
319if (NOT APPLE) 326if (NOT APPLE)
320 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 327 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
321endif() 328endif()
329
330if (ARCHITECTURE_x86_64)
331 target_link_libraries(yuzu PRIVATE dynarmic)
332endif()
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index cbcef7b45..eeff54359 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -19,7 +19,11 @@ AboutDialog::AboutDialog(QWidget* parent)
19 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 19 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
20 20
21 ui->setupUi(this); 21 ui->setupUi(this);
22 ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); 22 // Try and request the icon from Qt theme (Linux?)
23 const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
24 if (!yuzu_logo.isNull()) {
25 ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
26 }
23 ui->labelBuildInfo->setText( 27 ui->labelBuildInfo->setText(
24 ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version), 28 ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
25 QString::fromUtf8(Common::g_build_date).left(10))); 29 QString::fromUtf8(Common::g_build_date).left(10)));
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 2f7ddc7f3..1dd7b74bf 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -26,8 +26,20 @@
26 <verstretch>0</verstretch> 26 <verstretch>0</verstretch>
27 </sizepolicy> 27 </sizepolicy>
28 </property> 28 </property>
29 <property name="maximumSize">
30 <size>
31 <width>200</width>
32 <height>200</height>
33 </size>
34 </property>
29 <property name="text"> 35 <property name="text">
30 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 36 <string/>
37 </property>
38 <property name="pixmap">
39 <pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
40 </property>
41 <property name="scaledContents">
42 <bool>true</bool>
31 </property> 43 </property>
32 </widget> 44 </widget>
33 </item> 45 </item>
@@ -152,7 +164,7 @@ p, li { white-space: pre-wrap; }
152 </layout> 164 </layout>
153 </widget> 165 </widget>
154 <resources> 166 <resources>
155 <include location="../../dist/icons/icons.qrc"/> 167 <include location="../../dist/qt_themes_default/default/default.qrc"/>
156 </resources> 168 </resources>
157 <connections> 169 <connections>
158 <connection> 170 <connection>
diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp
index c924cb0cb..8be311fcb 100644
--- a/src/yuzu/applets/qt_controller.cpp
+++ b/src/yuzu/applets/qt_controller.cpp
@@ -631,7 +631,7 @@ void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
631 switch (max_supported_players) { 631 switch (max_supported_players) {
632 case 0: 632 case 0:
633 default: 633 default:
634 UNREACHABLE(); 634 ASSERT(false);
635 return; 635 return;
636 case 1: 636 case 1:
637 ui->widgetSpacer->hide(); 637 ui->widgetSpacer->hide();
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index d3cf0b43b..e8b217d90 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -411,11 +411,11 @@ void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
411 break; 411 break;
412 } 412 }
413 413
414 auto text = ui->topOSK->currentIndex() == 1 414 const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
415 ? ui->text_edit_osk->toPlainText().toStdU16String() 415 : ui->line_edit_osk->text();
416 : ui->line_edit_osk->text().toStdU16String(); 416 auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
417 417
418 emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true); 418 emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str), true);
419 break; 419 break;
420 } 420 }
421 } 421 }
@@ -562,7 +562,7 @@ void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
562 return; 562 return;
563 } 563 }
564 564
565 InlineTextInsertString(entered_text.toStdU16String()); 565 InlineTextInsertString(Common::U16StringFromBuffer(entered_text.utf16(), entered_text.size()));
566} 566}
567 567
568void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) { 568void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
@@ -1119,11 +1119,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
1119 } 1119 }
1120 1120
1121 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) { 1121 if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
1122 auto text = ui->topOSK->currentIndex() == 1 1122 const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
1123 ? ui->text_edit_osk->toPlainText().toStdU16String() 1123 : ui->line_edit_osk->text();
1124 : ui->line_edit_osk->text().toStdU16String(); 1124 auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
1125 1125
1126 emit SubmitNormalText(SwkbdResult::Ok, std::move(text)); 1126 emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str));
1127 return; 1127 return;
1128 } 1128 }
1129 1129
@@ -1189,7 +1189,8 @@ void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button)
1189 return; 1189 return;
1190 } 1190 }
1191 1191
1192 InlineTextInsertString(button->text().toStdU16String()); 1192 const auto button_text = button->text();
1193 InlineTextInsertString(Common::U16StringFromBuffer(button_text.utf16(), button_text.size()));
1193 1194
1194 // Revert the keyboard to lowercase if the shift key is active. 1195 // Revert the keyboard to lowercase if the shift key is active.
1195 if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) { 1196 if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
@@ -1282,11 +1283,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
1282 if (is_inline) { 1283 if (is_inline) {
1283 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position); 1284 emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
1284 } else { 1285 } else {
1285 auto text = ui->topOSK->currentIndex() == 1 1286 const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
1286 ? ui->text_edit_osk->toPlainText().toStdU16String() 1287 : ui->line_edit_osk->text();
1287 : ui->line_edit_osk->text().toStdU16String(); 1288 auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
1288 1289
1289 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text)); 1290 emit SubmitNormalText(SwkbdResult::Cancel, std::move(text_str));
1290 } 1291 }
1291 break; 1292 break;
1292 case Core::HID::NpadButton::Y: 1293 case Core::HID::NpadButton::Y:
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index a1b819ae0..cbe4e2daa 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -50,6 +50,7 @@ void EmuThread::run() {
50 50
51 auto& gpu = system.GPU(); 51 auto& gpu = system.GPU();
52 auto stop_token = stop_source.get_token(); 52 auto stop_token = stop_source.get_token();
53 bool debugger_should_start = system.DebuggerEnabled();
53 54
54 system.RegisterHostThread(); 55 system.RegisterHostThread();
55 56
@@ -89,6 +90,12 @@ void EmuThread::run() {
89 this->SetRunning(false); 90 this->SetRunning(false);
90 emit ErrorThrown(result, system.GetStatusDetails()); 91 emit ErrorThrown(result, system.GetStatusDetails());
91 } 92 }
93
94 if (debugger_should_start) {
95 system.InitializeDebugger();
96 debugger_should_start = false;
97 }
98
92 running_wait.Wait(); 99 running_wait.Wait();
93 result = system.Pause(); 100 result = system.Pause();
94 if (result != Core::SystemResultStatus::Success) { 101 if (result != Core::SystemResultStatus::Success) {
@@ -102,11 +109,9 @@ void EmuThread::run() {
102 was_active = true; 109 was_active = true;
103 emit DebugModeEntered(); 110 emit DebugModeEntered();
104 } 111 }
105 } else if (exec_step) {
106 UNIMPLEMENTED();
107 } else { 112 } else {
108 std::unique_lock lock{running_mutex}; 113 std::unique_lock lock{running_mutex};
109 running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; }); 114 running_cv.wait(lock, stop_token, [this] { return IsRunning(); });
110 } 115 }
111 } 116 }
112 117
@@ -122,7 +127,7 @@ void EmuThread::run() {
122class OpenGLSharedContext : public Core::Frontend::GraphicsContext { 127class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
123public: 128public:
124 /// Create the original context that should be shared from 129 /// Create the original context that should be shared from
125 explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { 130 explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
126 QSurfaceFormat format; 131 QSurfaceFormat format;
127 format.setVersion(4, 6); 132 format.setVersion(4, 6);
128 format.setProfile(QSurfaceFormat::CompatibilityProfile); 133 format.setProfile(QSurfaceFormat::CompatibilityProfile);
@@ -359,9 +364,9 @@ void GRenderWindow::RestoreGeometry() {
359 QWidget::restoreGeometry(geometry); 364 QWidget::restoreGeometry(geometry);
360} 365}
361 366
362void GRenderWindow::restoreGeometry(const QByteArray& geometry) { 367void GRenderWindow::restoreGeometry(const QByteArray& geometry_) {
363 // Make sure users of this class don't need to deal with backing up the geometry themselves 368 // Make sure users of this class don't need to deal with backing up the geometry themselves
364 QWidget::restoreGeometry(geometry); 369 QWidget::restoreGeometry(geometry_);
365 BackupGeometry(); 370 BackupGeometry();
366} 371}
367 372
@@ -747,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
747 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); 752 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
748 753
749 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
750 QCursor::setPos(mapToGlobal({center_x, center_y})); 755 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
751 } 756 }
752 757
753 emit MouseActivity(); 758 emit MouseActivity();
@@ -772,65 +777,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
772void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { 777void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
773 QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); 778 QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
774 for (const auto& touch_point : touch_points) { 779 for (const auto& touch_point : touch_points) {
775 if (!TouchUpdate(touch_point)) { 780 const auto [x, y] = ScaleTouch(touch_point.pos());
776 TouchStart(touch_point); 781 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
777 } 782 input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
778 } 783 }
779} 784}
780 785
781void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) { 786void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
782 QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints(); 787 QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
788 input_subsystem->GetTouchScreen()->ClearActiveFlag();
783 for (const auto& touch_point : touch_points) { 789 for (const auto& touch_point : touch_points) {
784 if (!TouchUpdate(touch_point)) { 790 const auto [x, y] = ScaleTouch(touch_point.pos());
785 TouchStart(touch_point); 791 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
786 } 792 input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
787 }
788 // Release all inactive points
789 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
790 if (!TouchExist(touch_ids[id], touch_points)) {
791 touch_ids[id] = 0;
792 input_subsystem->GetTouchScreen()->TouchReleased(id);
793 }
794 } 793 }
794 input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
795} 795}
796 796
797void GRenderWindow::TouchEndEvent() { 797void GRenderWindow::TouchEndEvent() {
798 for (std::size_t id = 0; id < touch_ids.size(); ++id) { 798 input_subsystem->GetTouchScreen()->ReleaseAllTouch();
799 if (touch_ids[id] != 0) {
800 touch_ids[id] = 0;
801 input_subsystem->GetTouchScreen()->TouchReleased(id);
802 }
803 }
804}
805
806void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
807 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
808 if (touch_ids[id] == 0) {
809 touch_ids[id] = touch_point.id() + 1;
810 const auto [x, y] = ScaleTouch(touch_point.pos());
811 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
812 input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
813 }
814 }
815}
816
817bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
818 for (std::size_t id = 0; id < touch_ids.size(); ++id) {
819 if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
820 const auto [x, y] = ScaleTouch(touch_point.pos());
821 const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
822 input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
823 return true;
824 }
825 }
826 return false;
827}
828
829bool GRenderWindow::TouchExist(std::size_t id,
830 const QList<QTouchEvent::TouchPoint>& touch_points) const {
831 return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
832 return id == static_cast<std::size_t>(point.id() + 1);
833 });
834} 799}
835 800
836bool GRenderWindow::event(QEvent* event) { 801bool GRenderWindow::event(QEvent* event) {
@@ -1049,8 +1014,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
1049 return unsupported_ext; 1014 return unsupported_ext;
1050} 1015}
1051 1016
1052void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { 1017void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) {
1053 this->emu_thread = emu_thread; 1018 emu_thread = emu_thread_;
1054} 1019}
1055 1020
1056void GRenderWindow::OnEmulationStopping() { 1021void GRenderWindow::OnEmulationStopping() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4b0ce0293..81fe52c0e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -10,6 +10,7 @@
10#include <mutex> 10#include <mutex>
11 11
12#include <QImage> 12#include <QImage>
13#include <QStringList>
13#include <QThread> 14#include <QThread>
14#include <QTouchEvent> 15#include <QTouchEvent>
15#include <QWidget> 16#include <QWidget>
@@ -20,7 +21,6 @@
20class GRenderWindow; 21class GRenderWindow;
21class GMainWindow; 22class GMainWindow;
22class QKeyEvent; 23class QKeyEvent;
23class QStringList;
24 24
25namespace Core { 25namespace Core {
26enum class SystemResultStatus : u32; 26enum class SystemResultStatus : u32;
@@ -55,22 +55,13 @@ public:
55 void run() override; 55 void run() override;
56 56
57 /** 57 /**
58 * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
59 * @note This function is thread-safe
60 */
61 void ExecStep() {
62 exec_step = true;
63 running_cv.notify_all();
64 }
65
66 /**
67 * Sets whether the emulation thread is running or not 58 * Sets whether the emulation thread is running or not
68 * @param running Boolean value, set the emulation thread to running if true 59 * @param running_ Boolean value, set the emulation thread to running if true
69 * @note This function is thread-safe 60 * @note This function is thread-safe
70 */ 61 */
71 void SetRunning(bool running) { 62 void SetRunning(bool running_) {
72 std::unique_lock lock{running_mutex}; 63 std::unique_lock lock{running_mutex};
73 this->running = running; 64 running = running_;
74 lock.unlock(); 65 lock.unlock();
75 running_cv.notify_all(); 66 running_cv.notify_all();
76 if (!running) { 67 if (!running) {
@@ -99,7 +90,6 @@ public:
99 } 90 }
100 91
101private: 92private:
102 bool exec_step = false;
103 bool running = false; 93 bool running = false;
104 std::stop_source stop_source; 94 std::stop_source stop_source;
105 std::mutex running_mutex; 95 std::mutex running_mutex;
@@ -148,8 +138,8 @@ public:
148 138
149 void BackupGeometry(); 139 void BackupGeometry();
150 void RestoreGeometry(); 140 void RestoreGeometry();
151 void restoreGeometry(const QByteArray& geometry); // overridden 141 void restoreGeometry(const QByteArray& geometry_); // overridden
152 QByteArray saveGeometry(); // overridden 142 QByteArray saveGeometry(); // overridden
153 143
154 qreal windowPixelRatio() const; 144 qreal windowPixelRatio() const;
155 145
@@ -199,7 +189,7 @@ public:
199 void Exit(); 189 void Exit();
200 190
201public slots: 191public slots:
202 void OnEmulationStarting(EmuThread* emu_thread); 192 void OnEmulationStarting(EmuThread* emu_thread_);
203 void OnEmulationStopping(); 193 void OnEmulationStopping();
204 void OnFramebufferSizeChanged(); 194 void OnFramebufferSizeChanged();
205 195
@@ -217,10 +207,6 @@ private:
217 void TouchUpdateEvent(const QTouchEvent* event); 207 void TouchUpdateEvent(const QTouchEvent* event);
218 void TouchEndEvent(); 208 void TouchEndEvent();
219 209
220 void TouchStart(const QTouchEvent::TouchPoint& touch_point);
221 bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
222 bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
223
224 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; 210 void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
225 211
226 bool InitializeOpenGL(); 212 bool InitializeOpenGL();
@@ -246,8 +232,6 @@ private:
246 bool first_frame = false; 232 bool first_frame = false;
247 InputCommon::TasInput::TasState last_tas_state; 233 InputCommon::TasInput::TasState last_tas_state;
248 234
249 std::array<std::size_t, 16> touch_ids{};
250
251 Core::System& system; 235 Core::System& system;
252 236
253protected: 237protected:
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp
new file mode 100644
index 000000000..e6d66ab34
--- /dev/null
+++ b/src/yuzu/check_vulkan.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/vulkan_common/vulkan_wrapper.h"
5
6#include <filesystem>
7#include <fstream>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "video_core/vulkan_common/vulkan_instance.h"
12#include "video_core/vulkan_common/vulkan_library.h"
13#include "yuzu/check_vulkan.h"
14#include "yuzu/uisettings.h"
15
16constexpr char TEMP_FILE_NAME[] = "vulkan_check";
17
18bool CheckVulkan() {
19 if (UISettings::values.has_broken_vulkan) {
20 return true;
21 }
22
23 LOG_DEBUG(Frontend, "Checking presence of Vulkan");
24
25 const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
26 const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
27
28 if (std::filesystem::exists(temp_file_loc)) {
29 LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
30
31 UISettings::values.has_broken_vulkan = true;
32 std::filesystem::remove(temp_file_loc);
33 return false;
34 }
35
36 std::ofstream temp_file_handle(temp_file_loc);
37 temp_file_handle.close();
38
39 try {
40 Vulkan::vk::InstanceDispatch dld;
41 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
42 const Vulkan::vk::Instance instance =
43 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
44
45 } catch (const Vulkan::vk::Exception& exception) {
46 LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
47 // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
48 // application, not when we can handle it.
49 }
50
51 std::filesystem::remove(temp_file_loc);
52 return true;
53}
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h
new file mode 100644
index 000000000..e4ea93582
--- /dev/null
+++ b/src/yuzu/check_vulkan.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6bool CheckVulkan();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index ac26b885b..9df4752be 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
71// UISetting::values.shortcuts, which is alphabetically ordered. 71// UISetting::values.shortcuts, which is alphabetically ordered.
72// clang-format off 72// clang-format off
73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
74 {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
75 {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
76 {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
77 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
78 {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
79 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
80 {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
81 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
82 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
83 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
84 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
85 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
86 {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
87 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
88 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
89 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
90 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
91 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
92 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
93 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
94 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
95 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 95 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
96}}; 96}};
97// clang-format on 97// clang-format on
98 98
@@ -525,6 +525,9 @@ void Config::ReadDebuggingValues() {
525 // Intentionally not using the QT default setting as this is intended to be changed in the ini 525 // Intentionally not using the QT default setting as this is intended to be changed in the ini
526 Settings::values.record_frame_times = 526 Settings::values.record_frame_times =
527 qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); 527 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
528
529 ReadBasicSetting(Settings::values.use_gdbstub);
530 ReadBasicSetting(Settings::values.gdbstub_port);
528 ReadBasicSetting(Settings::values.program_args); 531 ReadBasicSetting(Settings::values.program_args);
529 ReadBasicSetting(Settings::values.dump_exefs); 532 ReadBasicSetting(Settings::values.dump_exefs);
530 ReadBasicSetting(Settings::values.dump_nso); 533 ReadBasicSetting(Settings::values.dump_nso);
@@ -679,6 +682,12 @@ void Config::ReadRendererValues() {
679 ReadGlobalSetting(Settings::values.bg_green); 682 ReadGlobalSetting(Settings::values.bg_green);
680 ReadGlobalSetting(Settings::values.bg_blue); 683 ReadGlobalSetting(Settings::values.bg_blue);
681 684
685 if (!global && UISettings::values.has_broken_vulkan &&
686 Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
687 !Settings::values.renderer_backend.UsingGlobal()) {
688 Settings::values.renderer_backend.SetGlobal(true);
689 }
690
682 if (global) { 691 if (global) {
683 ReadBasicSetting(Settings::values.renderer_debug); 692 ReadBasicSetting(Settings::values.renderer_debug);
684 ReadBasicSetting(Settings::values.renderer_shader_feedback); 693 ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -798,6 +807,7 @@ void Config::ReadUIValues() {
798 ReadBasicSetting(UISettings::values.pause_when_in_background); 807 ReadBasicSetting(UISettings::values.pause_when_in_background);
799 ReadBasicSetting(UISettings::values.mute_when_in_background); 808 ReadBasicSetting(UISettings::values.mute_when_in_background);
800 ReadBasicSetting(UISettings::values.hide_mouse); 809 ReadBasicSetting(UISettings::values.hide_mouse);
810 ReadBasicSetting(UISettings::values.has_broken_vulkan);
801 ReadBasicSetting(UISettings::values.disable_web_applet); 811 ReadBasicSetting(UISettings::values.disable_web_applet);
802 812
803 qt_config->endGroup(); 813 qt_config->endGroup();
@@ -1095,6 +1105,8 @@ void Config::SaveDebuggingValues() {
1095 1105
1096 // Intentionally not using the QT default setting as this is intended to be changed in the ini 1106 // Intentionally not using the QT default setting as this is intended to be changed in the ini
1097 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); 1107 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
1108 WriteBasicSetting(Settings::values.use_gdbstub);
1109 WriteBasicSetting(Settings::values.gdbstub_port);
1098 WriteBasicSetting(Settings::values.program_args); 1110 WriteBasicSetting(Settings::values.program_args);
1099 WriteBasicSetting(Settings::values.dump_exefs); 1111 WriteBasicSetting(Settings::values.dump_exefs);
1100 WriteBasicSetting(Settings::values.dump_nso); 1112 WriteBasicSetting(Settings::values.dump_nso);
@@ -1343,6 +1355,7 @@ void Config::SaveUIValues() {
1343 WriteBasicSetting(UISettings::values.pause_when_in_background); 1355 WriteBasicSetting(UISettings::values.pause_when_in_background);
1344 WriteBasicSetting(UISettings::values.mute_when_in_background); 1356 WriteBasicSetting(UISettings::values.mute_when_in_background);
1345 WriteBasicSetting(UISettings::values.hide_mouse); 1357 WriteBasicSetting(UISettings::values.hide_mouse);
1358 WriteBasicSetting(UISettings::values.has_broken_vulkan);
1346 WriteBasicSetting(UISettings::values.disable_web_applet); 1359 WriteBasicSetting(UISettings::values.disable_web_applet);
1347 1360
1348 qt_config->endGroup(); 1361 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index bd50f7a68..343d2aee1 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,13 +24,18 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
24 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir)); 24 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
25 QDesktopServices::openUrl(QUrl::fromLocalFile(path)); 25 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
26 }); 26 });
27
28 connect(ui->toggle_gdbstub, &QCheckBox::toggled,
29 [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
27} 30}
28 31
29ConfigureDebug::~ConfigureDebug() = default; 32ConfigureDebug::~ConfigureDebug() = default;
30 33
31void ConfigureDebug::SetConfiguration() { 34void ConfigureDebug::SetConfiguration() {
32 const bool runtime_lock = !system.IsPoweredOn(); 35 const bool runtime_lock = !system.IsPoweredOn();
33 36 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
37 ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
38 ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
34 ui->toggle_console->setEnabled(runtime_lock); 39 ui->toggle_console->setEnabled(runtime_lock);
35 ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); 40 ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
36 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); 41 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
@@ -53,6 +58,8 @@ void ConfigureDebug::SetConfiguration() {
53 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue()); 58 ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
54 ui->dump_shaders->setEnabled(runtime_lock); 59 ui->dump_shaders->setEnabled(runtime_lock);
55 ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue()); 60 ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
61 ui->dump_macros->setEnabled(runtime_lock);
62 ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
56 ui->disable_macro_jit->setEnabled(runtime_lock); 63 ui->disable_macro_jit->setEnabled(runtime_lock);
57 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue()); 64 ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
58 ui->disable_loop_safety_checks->setEnabled(runtime_lock); 65 ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -69,6 +76,8 @@ void ConfigureDebug::SetConfiguration() {
69} 76}
70 77
71void ConfigureDebug::ApplyConfiguration() { 78void ConfigureDebug::ApplyConfiguration() {
79 Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
80 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
72 UISettings::values.show_console = ui->toggle_console->isChecked(); 81 UISettings::values.show_console = ui->toggle_console->isChecked();
73 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 82 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
74 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 83 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
@@ -83,6 +92,7 @@ void ConfigureDebug::ApplyConfiguration() {
83 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); 92 Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
84 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); 93 Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
85 Settings::values.dump_shaders = ui->dump_shaders->isChecked(); 94 Settings::values.dump_shaders = ui->dump_shaders->isChecked();
95 Settings::values.dump_macros = ui->dump_macros->isChecked();
86 Settings::values.disable_shader_loop_safety_checks = 96 Settings::values.disable_shader_loop_safety_checks =
87 ui->disable_loop_safety_checks->isChecked(); 97 ui->disable_loop_safety_checks->isChecked();
88 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked(); 98 Settings::values.disable_macro_jit = ui->disable_macro_jit->isChecked();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index c1d90d588..1152fa6c6 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -3,6 +3,60 @@
3 <class>ConfigureDebug</class> 3 <class>ConfigureDebug</class>
4 <widget class="QWidget" name="ConfigureDebug"> 4 <widget class="QWidget" name="ConfigureDebug">
5 <layout class="QVBoxLayout" name="verticalLayout_1"> 5 <layout class="QVBoxLayout" name="verticalLayout_1">
6 <item>
7 <layout class="QVBoxLayout" name="verticalLayout_2">
8 <item>
9 <widget class="QGroupBox" name="groupBox">
10 <property name="title">
11 <string>Debugger</string>
12 </property>
13 <layout class="QVBoxLayout" name="verticalLayout_3">
14 <item>
15 <layout class="QHBoxLayout" name="horizontalLayout_11">
16 <item>
17 <widget class="QCheckBox" name="toggle_gdbstub">
18 <property name="text">
19 <string>Enable GDB Stub</string>
20 </property>
21 </widget>
22 </item>
23 <item>
24 <spacer name="horizontalSpacer">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeHint" stdset="0">
29 <size>
30 <width>40</width>
31 <height>20</height>
32 </size>
33 </property>
34 </spacer>
35 </item>
36 <item>
37 <widget class="QLabel" name="label_11">
38 <property name="text">
39 <string>Port:</string>
40 </property>
41 </widget>
42 </item>
43 <item>
44 <widget class="QSpinBox" name="gdbport_spinbox">
45 <property name="minimum">
46 <number>1024</number>
47 </property>
48 <property name="maximum">
49 <number>65535</number>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </item>
55 </layout>
56 </widget>
57 </item>
58 </layout>
59 </item>
6 <item> 60 <item>
7 <widget class="QGroupBox" name="groupBox_2"> 61 <widget class="QGroupBox" name="groupBox_2">
8 <property name="title"> 62 <property name="title">
@@ -118,6 +172,19 @@
118 </property> 172 </property>
119 </widget> 173 </widget>
120 </item> 174 </item>
175 <item row="0" column="2">
176 <widget class="QCheckBox" name="dump_macros">
177 <property name="enabled">
178 <bool>true</bool>
179 </property>
180 <property name="toolTip">
181 <string>When checked, it will dump all the macro programs of the GPU</string>
182 </property>
183 <property name="text">
184 <string>Dump Maxwell Macros</string>
185 </property>
186 </widget>
187 </item>
121 <item row="0" column="1"> 188 <item row="0" column="1">
122 <widget class="QCheckBox" name="disable_macro_jit"> 189 <widget class="QCheckBox" name="disable_macro_jit">
123 <property name="enabled"> 190 <property name="enabled">
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index b415a1cc4..e99657bd6 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -27,12 +27,11 @@
27#include "yuzu/hotkeys.h" 27#include "yuzu/hotkeys.h"
28#include "yuzu/uisettings.h" 28#include "yuzu/uisettings.h"
29 29
30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 30ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
31 InputCommon::InputSubsystem* input_subsystem, 31 InputCommon::InputSubsystem* input_subsystem,
32 Core::System& system_) 32 Core::System& system_)
33 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, 33 : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
34 registry(registry), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, 34 system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, this)},
35 this)},
36 cpu_tab{std::make_unique<ConfigureCpu>(system_, this)}, 35 cpu_tab{std::make_unique<ConfigureCpu>(system_, this)},
37 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)}, 36 debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
38 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)}, 37 filesystem_tab{std::make_unique<ConfigureFilesystem>(this)},
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 32ddfd4e0..12cf25daf 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -40,7 +40,7 @@ class ConfigureDialog : public QDialog {
40 Q_OBJECT 40 Q_OBJECT
41 41
42public: 42public:
43 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, 43 explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
44 InputCommon::InputSubsystem* input_subsystem, Core::System& system_); 44 InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
45 ~ConfigureDialog() override; 45 ~ConfigureDialog() override;
46 46
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2f1435b10..85f34dc35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -17,6 +17,7 @@
17#include "video_core/vulkan_common/vulkan_library.h" 17#include "video_core/vulkan_common/vulkan_library.h"
18#include "yuzu/configuration/configuration_shared.h" 18#include "yuzu/configuration/configuration_shared.h"
19#include "yuzu/configuration/configure_graphics.h" 19#include "yuzu/configuration/configure_graphics.h"
20#include "yuzu/uisettings.h"
20 21
21ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) 22ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
22 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { 23 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
@@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
57 UpdateBackgroundColorButton(new_bg_color); 58 UpdateBackgroundColorButton(new_bg_color);
58 }); 59 });
59 60
61 connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
62 UISettings::values.has_broken_vulkan = false;
63
64 if (RetrieveVulkanDevices()) {
65 ui->api->setEnabled(true);
66 ui->button_check_vulkan->hide();
67
68 for (const auto& device : vulkan_devices) {
69 ui->device->addItem(device);
70 }
71 } else {
72 UISettings::values.has_broken_vulkan = true;
73 }
74 });
75
76 ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
77 ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
78
60 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 79 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
61 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 80 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
62} 81}
@@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
296 vulkan_device = Settings::values.vulkan_device.GetValue(true); 315 vulkan_device = Settings::values.vulkan_device.GetValue(true);
297 shader_backend = Settings::values.shader_backend.GetValue(true); 316 shader_backend = Settings::values.shader_backend.GetValue(true);
298 ui->device_widget->setEnabled(false); 317 ui->device_widget->setEnabled(false);
299 ui->backend_widget->setEnabled(false); 318 ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
300 } else { 319 } else {
301 vulkan_device = Settings::values.vulkan_device.GetValue(); 320 vulkan_device = Settings::values.vulkan_device.GetValue();
302 shader_backend = Settings::values.shader_backend.GetValue(); 321 shader_backend = Settings::values.shader_backend.GetValue();
@@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() {
318 } 337 }
319} 338}
320 339
321void ConfigureGraphics::RetrieveVulkanDevices() try { 340bool ConfigureGraphics::RetrieveVulkanDevices() try {
341 if (UISettings::values.has_broken_vulkan) {
342 return false;
343 }
344
322 using namespace Vulkan; 345 using namespace Vulkan;
323 346
324 vk::InstanceDispatch dld; 347 vk::InstanceDispatch dld;
@@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
333 vulkan_devices.push_back(QString::fromStdString(name)); 356 vulkan_devices.push_back(QString::fromStdString(name));
334 } 357 }
335 358
359 return true;
336} catch (const Vulkan::vk::Exception& exception) { 360} catch (const Vulkan::vk::Exception& exception) {
337 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 361 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
362 return false;
338} 363}
339 364
340Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 365Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
415 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 440 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
416 ConfigurationShared::InsertGlobalItem( 441 ConfigurationShared::InsertGlobalItem(
417 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 442 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
443
444 if (UISettings::values.has_broken_vulkan) {
445 ui->backend_widget->setEnabled(true);
446 ConfigurationShared::SetColoredComboBox(
447 ui->backend, ui->backend_widget,
448 static_cast<int>(Settings::values.shader_backend.GetValue(true)));
449 }
418} 450}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 1b101c940..8438f0187 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -41,7 +41,7 @@ private:
41 void UpdateDeviceSelection(int device); 41 void UpdateDeviceSelection(int device);
42 void UpdateShaderBackendSelection(int backend); 42 void UpdateShaderBackendSelection(int backend);
43 43
44 void RetrieveVulkanDevices(); 44 bool RetrieveVulkanDevices();
45 45
46 void SetupPerGameUI(); 46 void SetupPerGameUI();
47 47
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 74f0e0b79..2f94c94bc 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>437</width> 9 <width>471</width>
10 <height>482</height> 10 <height>759</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -171,11 +171,11 @@
171 </widget> 171 </widget>
172 </item> 172 </item>
173 <item> 173 <item>
174 <widget class="QCheckBox" name="accelerate_astc"> 174 <widget class="QCheckBox" name="accelerate_astc">
175 <property name="text"> 175 <property name="text">
176 <string>Accelerate ASTC texture decoding</string> 176 <string>Accelerate ASTC texture decoding</string>
177 </property> 177 </property>
178 </widget> 178 </widget>
179 </item> 179 </item>
180 <item> 180 <item>
181 <widget class="QWidget" name="nvdec_emulation_widget" native="true"> 181 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
@@ -438,43 +438,43 @@
438 </widget> 438 </widget>
439 </item> 439 </item>
440 <item> 440 <item>
441 <widget class="QWidget" name="anti_aliasing_layout" native="true"> 441 <widget class="QWidget" name="anti_aliasing_layout" native="true">
442 <layout class="QHBoxLayout" name="horizontalLayout_7"> 442 <layout class="QHBoxLayout" name="horizontalLayout_7">
443 <property name="leftMargin"> 443 <property name="leftMargin">
444 <number>0</number> 444 <number>0</number>
445 </property> 445 </property>
446 <property name="topMargin"> 446 <property name="topMargin">
447 <number>0</number> 447 <number>0</number>
448 </property> 448 </property>
449 <property name="rightMargin"> 449 <property name="rightMargin">
450 <number>0</number> 450 <number>0</number>
451 </property>
452 <property name="bottomMargin">
453 <number>0</number>
454 </property>
455 <item>
456 <widget class="QLabel" name="anti_aliasing_label">
457 <property name="text">
458 <string>Anti-Aliasing Method:</string>
459 </property>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
451 </property> 467 </property>
452 <property name="bottomMargin"> 468 </item>
453 <number>0</number> 469 <item>
470 <property name="text">
471 <string>FXAA</string>
454 </property> 472 </property>
455 <item> 473 </item>
456 <widget class="QLabel" name="anti_aliasing_label"> 474 </widget>
457 <property name="text"> 475 </item>
458 <string>Anti-Aliasing Method:</string> 476 </layout>
459 </property> 477 </widget>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
467 </property>
468 </item>
469 <item>
470 <property name="text">
471 <string>FXAA</string>
472 </property>
473 </item>
474 </widget>
475 </item>
476 </layout>
477 </widget>
478 </item> 478 </item>
479 <item> 479 <item>
480 <widget class="QWidget" name="bg_layout" native="true"> 480 <widget class="QWidget" name="bg_layout" native="true">
@@ -574,6 +574,13 @@
574 </property> 574 </property>
575 </spacer> 575 </spacer>
576 </item> 576 </item>
577 <item>
578 <widget class="QPushButton" name="button_check_vulkan">
579 <property name="text">
580 <string>Check for Working Vulkan</string>
581 </property>
582 </widget>
583 </item>
577 </layout> 584 </layout>
578 </widget> 585 </widget>
579 <resources/> 586 <resources/>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6679e9c53..edf0893c4 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
61 61
62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
63 for (const auto& group : registry.hotkey_groups) { 63 for (const auto& group : registry.hotkey_groups) {
64 auto* parent_item = new QStandardItem(group.first); 64 auto* parent_item =
65 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
65 parent_item->setEditable(false); 66 parent_item->setEditable(false);
67 parent_item->setData(group.first);
66 for (const auto& hotkey : group.second) { 68 for (const auto& hotkey : group.second) {
67 auto* action = new QStandardItem(hotkey.first); 69 auto* action =
70 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
68 auto* keyseq = 71 auto* keyseq =
69 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 72 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
70 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 73 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
71 action->setEditable(false); 74 action->setEditable(false);
75 action->setData(hotkey.first);
72 keyseq->setEditable(false); 76 keyseq->setEditable(false);
73 controller_keyseq->setEditable(false); 77 controller_keyseq->setEditable(false);
74 parent_item->appendRow({action, keyseq, controller_keyseq}); 78 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
93 ui->retranslateUi(this); 97 ui->retranslateUi(this);
94 98
95 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); 99 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
100 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
101 QStandardItem* parent = model->item(key_id, 0);
102 parent->setText(
103 QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
104 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
105 QStandardItem* action = parent->child(key_column_id, name_column);
106 action->setText(
107 QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
108 }
109 }
96} 110}
97 111
98void ConfigureHotkeys::Configure(QModelIndex index) { 112void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
273 const QStandardItem* controller_keyseq = 287 const QStandardItem* controller_keyseq =
274 parent->child(key_column_id, controller_column); 288 parent->child(key_column_id, controller_column);
275 for (auto& [group, sub_actions] : registry.hotkey_groups) { 289 for (auto& [group, sub_actions] : registry.hotkey_groups) {
276 if (group != parent->text()) 290 if (group != parent->data())
277 continue; 291 continue;
278 for (auto& [action_name, hotkey] : sub_actions) { 292 for (auto& [action_name, hotkey] : sub_actions) {
279 if (action_name != action->text()) 293 if (action_name != action->data())
280 continue; 294 continue;
281 hotkey.keyseq = QKeySequence(keyseq->text()); 295 hotkey.keyseq = QKeySequence(keyseq->text());
282 hotkey.controller_keyseq = controller_keyseq->text(); 296 hotkey.controller_keyseq = controller_keyseq->text();
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 1c05dd0f3..f3be9a374 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -264,15 +264,16 @@ QString ConfigureInputPlayer::AnalogToText(const Common::ParamPackage& param,
264 return QObject::tr("[unknown]"); 264 return QObject::tr("[unknown]");
265} 265}
266 266
267ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, 267ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index_,
268 QWidget* bottom_row, 268 QWidget* bottom_row_,
269 InputCommon::InputSubsystem* input_subsystem_, 269 InputCommon::InputSubsystem* input_subsystem_,
270 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_, 270 InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
271 bool is_powered_on_, bool debug) 271 bool is_powered_on_, bool debug_)
272 : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index), 272 : QWidget(parent),
273 debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, 273 ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index{player_index_}, debug{debug_},
274 profiles(profiles_), timeout_timer(std::make_unique<QTimer>()), 274 is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, profiles(profiles_),
275 poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} { 275 timeout_timer(std::make_unique<QTimer>()),
276 poll_timer(std::make_unique<QTimer>()), bottom_row{bottom_row_}, hid_core{hid_core_} {
276 if (player_index == 0) { 277 if (player_index == 0) {
277 auto* emulated_controller_p1 = 278 auto* emulated_controller_p1 =
278 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); 279 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
@@ -696,39 +697,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
696 UpdateControllerEnabledButtons(); 697 UpdateControllerEnabledButtons();
697 UpdateControllerButtonNames(); 698 UpdateControllerButtonNames();
698 UpdateMotionButtons(); 699 UpdateMotionButtons();
699 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), 700 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
700 [this, player_index](int) { 701 UpdateControllerAvailableButtons();
701 UpdateControllerAvailableButtons(); 702 UpdateControllerEnabledButtons();
702 UpdateControllerEnabledButtons(); 703 UpdateControllerButtonNames();
703 UpdateControllerButtonNames(); 704 UpdateMotionButtons();
704 UpdateMotionButtons(); 705 const Core::HID::NpadStyleIndex type =
705 const Core::HID::NpadStyleIndex type = 706 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
706 GetControllerTypeFromIndex(ui->comboControllerType->currentIndex()); 707
707 708 if (player_index == 0) {
708 if (player_index == 0) { 709 auto* emulated_controller_p1 =
709 auto* emulated_controller_p1 = 710 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
710 hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1); 711 auto* emulated_controller_handheld =
711 auto* emulated_controller_handheld = 712 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
712 hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld); 713 bool is_connected = emulated_controller->IsConnected(true);
713 bool is_connected = emulated_controller->IsConnected(true); 714
714 715 emulated_controller_p1->SetNpadStyleIndex(type);
715 emulated_controller_p1->SetNpadStyleIndex(type); 716 emulated_controller_handheld->SetNpadStyleIndex(type);
716 emulated_controller_handheld->SetNpadStyleIndex(type); 717 if (is_connected) {
717 if (is_connected) { 718 if (type == Core::HID::NpadStyleIndex::Handheld) {
718 if (type == Core::HID::NpadStyleIndex::Handheld) { 719 emulated_controller_p1->Disconnect();
719 emulated_controller_p1->Disconnect(); 720 emulated_controller_handheld->Connect(true);
720 emulated_controller_handheld->Connect(true); 721 emulated_controller = emulated_controller_handheld;
721 emulated_controller = emulated_controller_handheld; 722 } else {
722 } else { 723 emulated_controller_handheld->Disconnect();
723 emulated_controller_handheld->Disconnect(); 724 emulated_controller_p1->Connect(true);
724 emulated_controller_p1->Connect(true); 725 emulated_controller = emulated_controller_p1;
725 emulated_controller = emulated_controller_p1;
726 }
727 }
728 ui->controllerFrame->SetController(emulated_controller);
729 } 726 }
730 emulated_controller->SetNpadStyleIndex(type); 727 }
731 }); 728 ui->controllerFrame->SetController(emulated_controller);
729 }
730 emulated_controller->SetNpadStyleIndex(type);
731 });
732 732
733 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this, 733 connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
734 &ConfigureInputPlayer::UpdateMappingWithDefaults); 734 &ConfigureInputPlayer::UpdateMappingWithDefaults);
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 27559c37b..c313b0919 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() {
151 &ConfigureMotionTouch::OnConfigureTouchCalibration); 151 &ConfigureMotionTouch::OnConfigureTouchCalibration);
152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, 152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
153 &ConfigureMotionTouch::OnConfigureTouchFromButton); 153 &ConfigureMotionTouch::OnConfigureTouchFromButton);
154 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
155 &ConfigureMotionTouch::ApplyConfiguration);
154 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { 156 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
155 if (CanCloseDialog()) { 157 if (CanCloseDialog()) {
156 reject(); 158 reject();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index c75a84ae4..0237fae54 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -293,22 +293,5 @@
293 </layout> 293 </layout>
294 </widget> 294 </widget>
295 <resources/> 295 <resources/>
296 <connections> 296 <connections/>
297 <connection>
298 <sender>buttonBox</sender>
299 <signal>accepted()</signal>
300 <receiver>ConfigureMotionTouch</receiver>
301 <slot>ApplyConfiguration()</slot>
302 <hints>
303 <hint type="sourcelabel">
304 <x>20</x>
305 <y>20</y>
306 </hint>
307 <hint type="destinationlabel">
308 <x>20</x>
309 <y>20</y>
310 </hint>
311 </hints>
312 </connection>
313 </connections>
314</ui> 297</ui>
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 54b3fe150..af8343b2e 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -35,10 +35,10 @@
35#include "yuzu/uisettings.h" 35#include "yuzu/uisettings.h"
36#include "yuzu/util/util.h" 36#include "yuzu/util/util.h"
37 37
38ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, const std::string& file_name, 38ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
39 Core::System& system_) 39 Core::System& system_)
40 : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), 40 : QDialog(parent),
41 title_id(title_id), system{system_} { 41 ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} {
42 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); 42 const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
43 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) 43 const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
44 : fmt::format("{:016X}", title_id); 44 : fmt::format("{:016X}", title_id);
@@ -116,8 +116,8 @@ void ConfigurePerGame::HandleApplyButtonClicked() {
116 ApplyConfiguration(); 116 ApplyConfiguration();
117} 117}
118 118
119void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { 119void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) {
120 this->file = std::move(file); 120 file = std::move(file_);
121 LoadConfiguration(); 121 LoadConfiguration();
122} 122}
123 123
diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h
index e6dc05546..17a98a0f3 100644
--- a/src/yuzu/configuration/configure_per_game.h
+++ b/src/yuzu/configuration/configure_per_game.h
@@ -39,14 +39,14 @@ class ConfigurePerGame : public QDialog {
39 39
40public: 40public:
41 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 41 // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
42 explicit ConfigurePerGame(QWidget* parent, u64 title_id, const std::string& file_name, 42 explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
43 Core::System& system_); 43 Core::System& system_);
44 ~ConfigurePerGame() override; 44 ~ConfigurePerGame() override;
45 45
46 /// Save all button configurations to settings file 46 /// Save all button configurations to settings file
47 void ApplyConfiguration(); 47 void ApplyConfiguration();
48 48
49 void LoadFromFile(FileSys::VirtualFile file); 49 void LoadFromFile(FileSys::VirtualFile file_);
50 50
51private: 51private:
52 void changeEvent(QEvent* event) override; 52 void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp
index 7893a85bb..4906997ab 100644
--- a/src/yuzu/configuration/configure_per_game_addons.cpp
+++ b/src/yuzu/configuration/configure_per_game_addons.cpp
@@ -89,8 +89,8 @@ void ConfigurePerGameAddons::ApplyConfiguration() {
89 Settings::values.disabled_addons[title_id] = disabled_addons; 89 Settings::values.disabled_addons[title_id] = disabled_addons;
90} 90}
91 91
92void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) { 92void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file_) {
93 this->file = std::move(file); 93 file = std::move(file_);
94 LoadConfiguration(); 94 LoadConfiguration();
95} 95}
96 96
diff --git a/src/yuzu/configuration/configure_per_game_addons.h b/src/yuzu/configuration/configure_per_game_addons.h
index 24b017494..14690fba8 100644
--- a/src/yuzu/configuration/configure_per_game_addons.h
+++ b/src/yuzu/configuration/configure_per_game_addons.h
@@ -35,7 +35,7 @@ public:
35 /// Save all button configurations to settings file 35 /// Save all button configurations to settings file
36 void ApplyConfiguration(); 36 void ApplyConfiguration();
37 37
38 void LoadFromFile(FileSys::VirtualFile file); 38 void LoadFromFile(FileSys::VirtualFile file_);
39 39
40 void SetTitleId(u64 id); 40 void SetTitleId(u64 id);
41 41
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 4fcc22b7a..688c2dd38 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -165,10 +165,10 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
165 const std::string invert_str = invert_value ? "+" : "-"; 165 const std::string invert_str = invert_value ? "+" : "-";
166 param.Set("invert_x", invert_str); 166 param.Set("invert_x", invert_str);
167 emulated_device->SetRingParam(param); 167 emulated_device->SetRingParam(param);
168 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; 168 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
169 ++sub_button_id) { 169 ++sub_button_id2) {
170 analog_map_buttons[sub_button_id]->setText( 170 analog_map_buttons[sub_button_id2]->setText(
171 AnalogToText(param, analog_sub_buttons[sub_button_id])); 171 AnalogToText(param, analog_sub_buttons[sub_button_id2]));
172 } 172 }
173 }); 173 });
174 context_menu.exec( 174 context_menu.exec(
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 19aa589f9..ecebb0fb7 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() {
130 // Guard if during game and set to game-specific value 130 // Guard if during game and set to game-specific value
131 if (Settings::values.rng_seed.UsingGlobal()) { 131 if (Settings::values.rng_seed.UsingGlobal()) {
132 if (ui->rng_seed_checkbox->isChecked()) { 132 if (ui->rng_seed_checkbox->isChecked()) {
133 Settings::values.rng_seed.SetValue( 133 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
134 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
135 } else { 134 } else {
136 Settings::values.rng_seed.SetValue(std::nullopt); 135 Settings::values.rng_seed.SetValue(std::nullopt);
137 } 136 }
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
142 case ConfigurationShared::CheckState::Off: 141 case ConfigurationShared::CheckState::Off:
143 Settings::values.rng_seed.SetGlobal(false); 142 Settings::values.rng_seed.SetGlobal(false);
144 if (ui->rng_seed_checkbox->isChecked()) { 143 if (ui->rng_seed_checkbox->isChecked()) {
145 Settings::values.rng_seed.SetValue( 144 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
146 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
147 } else { 145 } else {
148 Settings::values.rng_seed.SetValue(std::nullopt); 146 Settings::values.rng_seed.SetValue(std::nullopt);
149 } 147 }
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
index c17da6fd1..06cc452c3 100644
--- a/src/yuzu/configuration/configure_touch_from_button.cpp
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -68,10 +68,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
68} 68}
69 69
70ConfigureTouchFromButton::ConfigureTouchFromButton( 70ConfigureTouchFromButton::ConfigureTouchFromButton(
71 QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps, 71 QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps_,
72 InputCommon::InputSubsystem* input_subsystem_, const int default_index) 72 InputCommon::InputSubsystem* input_subsystem_, const int default_index)
73 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()), 73 : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()),
74 touch_maps(touch_maps), input_subsystem{input_subsystem_}, selected_index(default_index), 74 touch_maps{touch_maps_}, input_subsystem{input_subsystem_}, selected_index{default_index},
75 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) { 75 timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
76 ui->setupUi(this); 76 ui->setupUi(this);
77 binding_list_model = new QStandardItemModel(0, 3, this); 77 binding_list_model = new QStandardItemModel(0, 3, this);
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
index e1400481a..b8c55db66 100644
--- a/src/yuzu/configuration/configure_touch_from_button.h
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -37,7 +37,7 @@ class ConfigureTouchFromButton : public QDialog {
37 37
38public: 38public:
39 explicit ConfigureTouchFromButton(QWidget* parent, 39 explicit ConfigureTouchFromButton(QWidget* parent,
40 const std::vector<Settings::TouchFromButtonMap>& touch_maps, 40 const std::vector<Settings::TouchFromButtonMap>& touch_maps_,
41 InputCommon::InputSubsystem* input_subsystem_, 41 InputCommon::InputSubsystem* input_subsystem_,
42 int default_index = 0); 42 int default_index = 0);
43 ~ConfigureTouchFromButton() override; 43 ~ConfigureTouchFromButton() override;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 8f486a131..0ea31cd33 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -113,9 +113,9 @@ QString WaitTreeText::GetText() const {
113 return text; 113 return text;
114} 114}
115 115
116WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table, 116WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address_, const Kernel::KHandleTable& handle_table,
117 Core::System& system_) 117 Core::System& system_)
118 : mutex_address(mutex_address), system{system_} { 118 : mutex_address{mutex_address_}, system{system_} {
119 mutex_value = system.Memory().Read32(mutex_address); 119 mutex_value = system.Memory().Read32(mutex_address);
120 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); 120 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);
121 owner = handle_table.GetObject<Kernel::KThread>(owner_handle).GetPointerUnsafe(); 121 owner = handle_table.GetObject<Kernel::KThread>(owner_handle).GetPointerUnsafe();
@@ -140,8 +140,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons
140 return list; 140 return list;
141} 141}
142 142
143WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread, Core::System& system_) 143WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_)
144 : thread(thread), system{system_} {} 144 : thread{thread_}, system{system_} {}
145WaitTreeCallstack::~WaitTreeCallstack() = default; 145WaitTreeCallstack::~WaitTreeCallstack() = default;
146 146
147QString WaitTreeCallstack::GetText() const { 147QString WaitTreeCallstack::GetText() const {
@@ -171,8 +171,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
171} 171}
172 172
173WaitTreeSynchronizationObject::WaitTreeSynchronizationObject( 173WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(
174 const Kernel::KSynchronizationObject& o, Core::System& system_) 174 const Kernel::KSynchronizationObject& object_, Core::System& system_)
175 : object(o), system{system_} {} 175 : object{object_}, system{system_} {}
176WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; 176WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default;
177 177
178WaitTreeExpandableItem::WaitTreeExpandableItem() = default; 178WaitTreeExpandableItem::WaitTreeExpandableItem() = default;
@@ -380,8 +380,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
380 return list; 380 return list;
381} 381}
382 382
383WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object, Core::System& system_) 383WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_)
384 : WaitTreeSynchronizationObject(object, system_) {} 384 : WaitTreeSynchronizationObject(object_, system_) {}
385WaitTreeEvent::~WaitTreeEvent() = default; 385WaitTreeEvent::~WaitTreeEvent() = default;
386 386
387WaitTreeThreadList::WaitTreeThreadList(std::vector<Kernel::KThread*>&& list, Core::System& system_) 387WaitTreeThreadList::WaitTreeThreadList(std::vector<Kernel::KThread*>&& list, Core::System& system_)
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index 4a36dfc48..f21b9f467 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -78,7 +78,7 @@ public:
78class WaitTreeMutexInfo : public WaitTreeExpandableItem { 78class WaitTreeMutexInfo : public WaitTreeExpandableItem {
79 Q_OBJECT 79 Q_OBJECT
80public: 80public:
81 explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table, 81 explicit WaitTreeMutexInfo(VAddr mutex_address_, const Kernel::KHandleTable& handle_table,
82 Core::System& system_); 82 Core::System& system_);
83 ~WaitTreeMutexInfo() override; 83 ~WaitTreeMutexInfo() override;
84 84
@@ -97,7 +97,7 @@ private:
97class WaitTreeCallstack : public WaitTreeExpandableItem { 97class WaitTreeCallstack : public WaitTreeExpandableItem {
98 Q_OBJECT 98 Q_OBJECT
99public: 99public:
100 explicit WaitTreeCallstack(const Kernel::KThread& thread, Core::System& system_); 100 explicit WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_);
101 ~WaitTreeCallstack() override; 101 ~WaitTreeCallstack() override;
102 102
103 QString GetText() const override; 103 QString GetText() const override;
@@ -112,7 +112,7 @@ private:
112class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { 112class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {
113 Q_OBJECT 113 Q_OBJECT
114public: 114public:
115 explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object, 115 explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object_,
116 Core::System& system_); 116 Core::System& system_);
117 ~WaitTreeSynchronizationObject() override; 117 ~WaitTreeSynchronizationObject() override;
118 118
@@ -162,7 +162,7 @@ private:
162class WaitTreeEvent : public WaitTreeSynchronizationObject { 162class WaitTreeEvent : public WaitTreeSynchronizationObject {
163 Q_OBJECT 163 Q_OBJECT
164public: 164public:
165 explicit WaitTreeEvent(const Kernel::KReadableEvent& object, Core::System& system_); 165 explicit WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_);
166 ~WaitTreeEvent() override; 166 ~WaitTreeEvent() override;
167}; 167};
168 168
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 4a6d74a7e..05d309827 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -28,8 +28,8 @@
28#include "yuzu/uisettings.h" 28#include "yuzu/uisettings.h"
29#include "yuzu/util/controller_navigation.h" 29#include "yuzu/util/controller_navigation.h"
30 30
31GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) 31GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent)
32 : QObject(parent), gamelist{gamelist} {} 32 : QObject(parent), gamelist{gamelist_} {}
33 33
34// EventFilter in order to process systemkeys while editing the searchfield 34// EventFilter in order to process systemkeys while editing the searchfield
35bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { 35bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) {
@@ -80,9 +80,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
80 return QObject::eventFilter(obj, event); 80 return QObject::eventFilter(obj, event);
81} 81}
82 82
83void GameListSearchField::setFilterResult(int visible, int total) { 83void GameListSearchField::setFilterResult(int visible_, int total_) {
84 this->visible = visible; 84 visible = visible_;
85 this->total = total; 85 total = total_;
86 86
87 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); 87 label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
88} 88}
@@ -309,9 +309,9 @@ void GameList::OnFilterCloseClicked() {
309 main_window->filterBarSetChecked(false); 309 main_window->filterBarSetChecked(false);
310} 310}
311 311
312GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider, 312GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_,
313 Core::System& system_, GMainWindow* parent) 313 Core::System& system_, GMainWindow* parent)
314 : QWidget{parent}, vfs(std::move(vfs)), provider(provider), system{system_} { 314 : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, system{system_} {
315 watcher = new QFileSystemWatcher(this); 315 watcher = new QFileSystemWatcher(this);
316 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory); 316 connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
317 317
@@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
483 // Also artificially caps the watcher to a certain number of directories 483 // Also artificially caps the watcher to a certain number of directories
484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000; 484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
485 constexpr int SLICE_SIZE = 25; 485 constexpr int SLICE_SIZE = 25;
486 int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); 486 int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
487 for (int i = 0; i < len; i += SLICE_SIZE) { 487 for (int i = 0; i < len; i += SLICE_SIZE) {
488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); 488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE));
489 QCoreApplication::processEvents(); 489 QCoreApplication::processEvents();
@@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent}
870 layout->setAlignment(Qt::AlignCenter); 870 layout->setAlignment(Qt::AlignCenter);
871 image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); 871 image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
872 872
873 text->setText(tr("Double-click to add a new folder to the game list")); 873 RetranslateUI();
874 QFont font = text->font(); 874 QFont font = text->font();
875 font.setPointSize(20); 875 font.setPointSize(20);
876 text->setFont(font); 876 text->setFont(font);
@@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() {
891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { 891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
892 emit GameListPlaceholder::AddDirectory(); 892 emit GameListPlaceholder::AddDirectory();
893} 893}
894
895void GameListPlaceholder::changeEvent(QEvent* event) {
896 if (event->type() == QEvent::LanguageChange) {
897 RetranslateUI();
898 }
899
900 QWidget::changeEvent(event);
901}
902
903void GameListPlaceholder::RetranslateUI() {
904 text->setText(tr("Double-click to add a new folder to the game list"));
905}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index d19dbe4b0..bc36d015a 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -67,8 +67,8 @@ public:
67 COLUMN_COUNT, // Number of columns 67 COLUMN_COUNT, // Number of columns
68 }; 68 };
69 69
70 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, 70 explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
71 FileSys::ManualContentProvider* provider, Core::System& system_, 71 FileSys::ManualContentProvider* provider_, Core::System& system_,
72 GMainWindow* parent = nullptr); 72 GMainWindow* parent = nullptr);
73 ~GameList() override; 73 ~GameList() override;
74 74
@@ -166,6 +166,9 @@ protected:
166 void mouseDoubleClickEvent(QMouseEvent* event) override; 166 void mouseDoubleClickEvent(QMouseEvent* event) override;
167 167
168private: 168private:
169 void changeEvent(QEvent* event) override;
170 void RetranslateUI();
171
169 QVBoxLayout* layout = nullptr; 172 QVBoxLayout* layout = nullptr;
170 QLabel* image = nullptr; 173 QLabel* image = nullptr;
171 QLabel* text = nullptr; 174 QLabel* text = nullptr;
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index f2a986ed8..cd7d63536 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -225,8 +225,8 @@ public:
225 static constexpr int GameDirRole = Qt::UserRole + 2; 225 static constexpr int GameDirRole = Qt::UserRole + 2;
226 226
227 explicit GameListDir(UISettings::GameDir& directory, 227 explicit GameListDir(UISettings::GameDir& directory,
228 GameListItemType dir_type = GameListItemType::CustomDir) 228 GameListItemType dir_type_ = GameListItemType::CustomDir)
229 : dir_type{dir_type} { 229 : dir_type{dir_type_} {
230 setData(type(), TypeRole); 230 setData(type(), TypeRole);
231 231
232 UISettings::GameDir* game_dir = &directory; 232 UISettings::GameDir* game_dir = &directory;
@@ -348,7 +348,7 @@ public:
348 explicit GameListSearchField(GameList* parent = nullptr); 348 explicit GameListSearchField(GameList* parent = nullptr);
349 349
350 QString filterText() const; 350 QString filterText() const;
351 void setFilterResult(int visible, int total); 351 void setFilterResult(int visible_, int total_);
352 352
353 void clear(); 353 void clear();
354 void setFocus(); 354 void setFocus();
@@ -356,7 +356,7 @@ public:
356private: 356private:
357 class KeyReleaseEater : public QObject { 357 class KeyReleaseEater : public QObject {
358 public: 358 public:
359 explicit KeyReleaseEater(GameList* gamelist, QObject* parent = nullptr); 359 explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr);
360 360
361 private: 361 private:
362 GameList* gamelist = nullptr; 362 GameList* gamelist = nullptr;
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index ca1899b5c..63326968b 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -223,12 +223,12 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri
223} 223}
224} // Anonymous namespace 224} // Anonymous namespace
225 225
226GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs, 226GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_,
227 FileSys::ManualContentProvider* provider, 227 FileSys::ManualContentProvider* provider_,
228 QVector<UISettings::GameDir>& game_dirs, 228 QVector<UISettings::GameDir>& game_dirs_,
229 const CompatibilityList& compatibility_list, Core::System& system_) 229 const CompatibilityList& compatibility_list_, Core::System& system_)
230 : vfs(std::move(vfs)), provider(provider), game_dirs(game_dirs), 230 : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_},
231 compatibility_list(compatibility_list), system{system_} {} 231 compatibility_list{compatibility_list_}, system{system_} {}
232 232
233GameListWorker::~GameListWorker() = default; 233GameListWorker::~GameListWorker() = default;
234 234
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 622d241fb..24a4e92c3 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -33,10 +33,10 @@ class GameListWorker : public QObject, public QRunnable {
33 Q_OBJECT 33 Q_OBJECT
34 34
35public: 35public:
36 explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs, 36 explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
37 FileSys::ManualContentProvider* provider, 37 FileSys::ManualContentProvider* provider_,
38 QVector<UISettings::GameDir>& game_dirs, 38 QVector<UISettings::GameDir>& game_dirs_,
39 const CompatibilityList& compatibility_list, Core::System& system_); 39 const CompatibilityList& compatibility_list_, Core::System& system_);
40 ~GameListWorker() override; 40 ~GameListWorker() override;
41 41
42 /// Starts the processing of directory tree information. 42 /// Starts the processing of directory tree information.
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index edfb946a8..e273744fd 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
183 183
184void LoadingScreen::paintEvent(QPaintEvent* event) { 184void LoadingScreen::paintEvent(QPaintEvent* event) {
185 QStyleOption opt; 185 QStyleOption opt;
186 opt.init(this); 186 opt.initFrom(this);
187 QPainter p(this); 187 QPainter p(this);
188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); 188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
189 QWidget::paintEvent(event); 189 QWidget::paintEvent(event);
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
index 7c960ee72..17045595d 100644
--- a/src/yuzu/loading_screen.h
+++ b/src/yuzu/loading_screen.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <QString> 8#include <QString>
9#include <QWidget> 9#include <QWidget>
10#include <QtGlobal>
10 11
11#if !QT_CONFIG(movie) 12#if !QT_CONFIG(movie)
12#define YUZU_QT_MOVIE_MISSING 1 13#define YUZU_QT_MOVIE_MISSING 1
@@ -88,4 +89,6 @@ private:
88 std::size_t slow_shader_first_value = 0; 89 std::size_t slow_shader_first_value = 0;
89}; 90};
90 91
92#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
91Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); 93Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
94#endif
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f607f464a..b460020b1 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -52,7 +52,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
52#define QT_NO_OPENGL 52#define QT_NO_OPENGL
53#include <QClipboard> 53#include <QClipboard>
54#include <QDesktopServices> 54#include <QDesktopServices>
55#include <QDesktopWidget>
56#include <QFile> 55#include <QFile>
57#include <QFileDialog> 56#include <QFileDialog>
58#include <QInputDialog> 57#include <QInputDialog>
@@ -60,6 +59,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
60#include <QProgressBar> 59#include <QProgressBar>
61#include <QProgressDialog> 60#include <QProgressDialog>
62#include <QPushButton> 61#include <QPushButton>
62#include <QScreen>
63#include <QShortcut> 63#include <QShortcut>
64#include <QStatusBar> 64#include <QStatusBar>
65#include <QString> 65#include <QString>
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
115#include "video_core/shader_notify.h" 115#include "video_core/shader_notify.h"
116#include "yuzu/about_dialog.h" 116#include "yuzu/about_dialog.h"
117#include "yuzu/bootmanager.h" 117#include "yuzu/bootmanager.h"
118#include "yuzu/check_vulkan.h"
118#include "yuzu/compatdb.h" 119#include "yuzu/compatdb.h"
119#include "yuzu/compatibility_list.h" 120#include "yuzu/compatibility_list.h"
120#include "yuzu/configuration/config.h" 121#include "yuzu/configuration/config.h"
@@ -198,6 +199,59 @@ static void RemoveCachedContents() {
198 Common::FS::RemoveDirRecursively(offline_system_data); 199 Common::FS::RemoveDirRecursively(offline_system_data);
199} 200}
200 201
202static void LogRuntimes() {
203#ifdef _MSC_VER
204 // It is possible that the name of the dll will change.
205 // vcruntime140.dll is for 2015 and onwards
206 constexpr char runtime_dll_name[] = "vcruntime140.dll";
207 UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
208 bool runtime_version_inspection_worked = false;
209 if (sz > 0) {
210 std::vector<u8> buf(sz);
211 if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
212 VS_FIXEDFILEINFO* pvi;
213 sz = sizeof(VS_FIXEDFILEINFO);
214 if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
215 if (pvi->dwSignature == VS_FFI_SIGNATURE) {
216 runtime_version_inspection_worked = true;
217 LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
218 pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
219 pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
220 }
221 }
222 }
223 }
224 if (!runtime_version_inspection_worked) {
225 LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
226 }
227#endif
228}
229
230static QString PrettyProductName() {
231#ifdef _WIN32
232 // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
233 // With that notation change they changed the registry key used to denote the current version
234 QSettings windows_registry(
235 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
236 QSettings::NativeFormat);
237 const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
238 if (release_id == QStringLiteral("2009")) {
239 const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
240 const QString display_version =
241 windows_registry.value(QStringLiteral("DisplayVersion")).toString();
242 const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
243 u32 version = 10;
244 if (current_build >= 22000) {
245 version = 11;
246 }
247 return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
248 .arg(QString::number(version), display_version, QString::number(current_build),
249 QString::number(ubr));
250 }
251#endif
252 return QSysInfo::prettyProductName();
253}
254
201GMainWindow::GMainWindow() 255GMainWindow::GMainWindow()
202 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 256 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
203 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 257 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -243,6 +297,7 @@ GMainWindow::GMainWindow()
243 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 297 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
244 298
245 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version); 299 LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
300 LogRuntimes();
246#ifdef ARCHITECTURE_x86_64 301#ifdef ARCHITECTURE_x86_64
247 const auto& caps = Common::GetCPUCaps(); 302 const auto& caps = Common::GetCPUCaps();
248 std::string cpu_string = caps.cpu_string; 303 std::string cpu_string = caps.cpu_string;
@@ -259,7 +314,7 @@ GMainWindow::GMainWindow()
259 } 314 }
260 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 315 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
261#endif 316#endif
262 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 317 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
263 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 318 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
264 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 319 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
265 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); 320 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -297,6 +352,23 @@ GMainWindow::GMainWindow()
297 352
298 MigrateConfigFiles(); 353 MigrateConfigFiles();
299 354
355 if (!CheckVulkan()) {
356 config->Save();
357
358 QMessageBox::warning(
359 this, tr("Broken Vulkan Installation Detected"),
360 tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
361 "href='https://yuzu-emu.org/wiki/faq/"
362 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
363 "instructions to fix the issue</a>."));
364 }
365 if (UISettings::values.has_broken_vulkan) {
366 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
367
368 renderer_status_button->setDisabled(true);
369 renderer_status_button->setChecked(false);
370 }
371
300#if defined(HAVE_SDL2) && !defined(_WIN32) 372#if defined(HAVE_SDL2) && !defined(_WIN32)
301 SDL_InitSubSystem(SDL_INIT_VIDEO); 373 SDL_InitSubSystem(SDL_INIT_VIDEO);
302 // SDL disables the screen saver by default, and setting the hint 374 // SDL disables the screen saver by default, and setting the hint
@@ -827,12 +899,11 @@ void GMainWindow::InitializeWidgets() {
827 899
828 // Setup Dock button 900 // Setup Dock button
829 dock_status_button = new QPushButton(); 901 dock_status_button = new QPushButton();
830 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 902 dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
831 dock_status_button->setFocusPolicy(Qt::NoFocus); 903 dock_status_button->setFocusPolicy(Qt::NoFocus);
832 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); 904 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
833 dock_status_button->setText(tr("DOCK"));
834 dock_status_button->setCheckable(true); 905 dock_status_button->setCheckable(true);
835 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 906 UpdateDockedButton();
836 statusBar()->insertPermanentWidget(0, dock_status_button); 907 statusBar()->insertPermanentWidget(0, dock_status_button);
837 908
838 gpu_accuracy_button = new QPushButton(); 909 gpu_accuracy_button = new QPushButton();
@@ -863,8 +934,7 @@ void GMainWindow::InitializeWidgets() {
863 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan); 934 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
864 } else { 935 } else {
865 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL); 936 Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
866 const auto filter = Settings::values.scaling_filter.GetValue(); 937 if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
867 if (filter == Settings::ScalingFilter::Fsr) {
868 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor); 938 Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
869 UpdateFilterText(); 939 UpdateFilterText();
870 } 940 }
@@ -1002,7 +1072,7 @@ void GMainWindow::InitializeHotkeys() {
1002 1072
1003void GMainWindow::SetDefaultUIGeometry() { 1073void GMainWindow::SetDefaultUIGeometry() {
1004 // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half 1074 // geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
1005 const QRect screenRect = QApplication::desktop()->screenGeometry(this); 1075 const QRect screenRect = QGuiApplication::primaryScreen()->geometry();
1006 1076
1007 const int w = screenRect.width() * 2 / 3; 1077 const int w = screenRect.width() * 2 / 3;
1008 const int h = screenRect.height() * 2 / 3; 1078 const int h = screenRect.height() * 2 / 3;
@@ -1015,6 +1085,10 @@ void GMainWindow::SetDefaultUIGeometry() {
1015void GMainWindow::RestoreUIState() { 1085void GMainWindow::RestoreUIState() {
1016 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); 1086 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
1017 restoreGeometry(UISettings::values.geometry); 1087 restoreGeometry(UISettings::values.geometry);
1088 // Work-around because the games list isn't supposed to be full screen
1089 if (isFullScreen()) {
1090 showNormal();
1091 }
1018 restoreState(UISettings::values.state); 1092 restoreState(UISettings::values.state);
1019 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); 1093 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
1020 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 1094 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -1367,7 +1441,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1367 } 1441 }
1368 return false; 1442 return false;
1369 } 1443 }
1370 game_path = filename; 1444 current_game_path = filename;
1371 1445
1372 system->TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt"); 1446 system->TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
1373 return true; 1447 return true;
@@ -1401,7 +1475,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1401 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && 1475 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
1402 type == StartGameType::Normal) { 1476 type == StartGameType::Normal) {
1403 // Load per game settings 1477 // Load per game settings
1404 const auto file_path = std::filesystem::path{filename.toStdU16String()}; 1478 const auto file_path =
1479 std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
1405 const auto config_file_name = title_id == 0 1480 const auto config_file_name = title_id == 0
1406 ? Common::FS::PathToUTF8String(file_path.filename()) 1481 ? Common::FS::PathToUTF8String(file_path.filename())
1407 : fmt::format("{:016X}", title_id); 1482 : fmt::format("{:016X}", title_id);
@@ -1432,7 +1507,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1432 1507
1433 // Register an ExecuteProgram callback such that Core can execute a sub-program 1508 // Register an ExecuteProgram callback such that Core can execute a sub-program
1434 system->RegisterExecuteProgramCallback( 1509 system->RegisterExecuteProgramCallback(
1435 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); }); 1510 [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
1436 1511
1437 // Register an Exit callback such that Core can exit the currently running application. 1512 // Register an Exit callback such that Core can exit the currently running application.
1438 system->RegisterExitCallback([this]() { render_window->Exit(); }); 1513 system->RegisterExitCallback([this]() { render_window->Exit(); });
@@ -1482,7 +1557,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
1482 } 1557 }
1483 if (res != Loader::ResultStatus::Success || title_name.empty()) { 1558 if (res != Loader::ResultStatus::Success || title_name.empty()) {
1484 title_name = Common::FS::PathToUTF8String( 1559 title_name = Common::FS::PathToUTF8String(
1485 std::filesystem::path{filename.toStdU16String()}.filename()); 1560 std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
1561 .filename());
1486 } 1562 }
1487 const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess(); 1563 const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
1488 const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)"); 1564 const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
@@ -1514,6 +1590,7 @@ void GMainWindow::ShutdownGame() {
1514 1590
1515 AllowOSSleep(); 1591 AllowOSSleep();
1516 1592
1593 system->DetachDebugger();
1517 discord_rpc->Pause(); 1594 discord_rpc->Pause();
1518 emu_thread->RequestStop(); 1595 emu_thread->RequestStop();
1519 1596
@@ -1561,9 +1638,9 @@ void GMainWindow::ShutdownGame() {
1561 emu_speed_label->setVisible(false); 1638 emu_speed_label->setVisible(false);
1562 game_fps_label->setVisible(false); 1639 game_fps_label->setVisible(false);
1563 emu_frametime_label->setVisible(false); 1640 emu_frametime_label->setVisible(false);
1564 renderer_status_button->setEnabled(true); 1641 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
1565 1642
1566 game_path.clear(); 1643 current_game_path.clear();
1567 1644
1568 // When closing the game, destroy the GLWindow to clear the context after the game is closed 1645 // When closing the game, destroy the GLWindow to clear the context after the game is closed
1569 render_window->ReleaseRenderTarget(); 1646 render_window->ReleaseRenderTarget();
@@ -1581,7 +1658,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
1581 1658
1582void GMainWindow::UpdateRecentFiles() { 1659void GMainWindow::UpdateRecentFiles() {
1583 const int num_recent_files = 1660 const int num_recent_files =
1584 std::min(UISettings::values.recent_files.size(), max_recent_files_item); 1661 std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
1585 1662
1586 for (int i = 0; i < num_recent_files; i++) { 1663 for (int i = 0; i < num_recent_files; i++) {
1587 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( 1664 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2482,7 +2559,7 @@ void GMainWindow::OnRestartGame() {
2482 return; 2559 return;
2483 } 2560 }
2484 // Make a copy since BootGame edits game_path 2561 // Make a copy since BootGame edits game_path
2485 BootGame(QString(game_path)); 2562 BootGame(QString(current_game_path));
2486} 2563}
2487 2564
2488void GMainWindow::OnPauseGame() { 2565void GMainWindow::OnPauseGame() {
@@ -2579,6 +2656,18 @@ void GMainWindow::ToggleFullscreen() {
2579 } 2656 }
2580} 2657}
2581 2658
2659// We're going to return the screen that the given window has the most pixels on
2660static QScreen* GuessCurrentScreen(QWidget* window) {
2661 const QList<QScreen*> screens = QGuiApplication::screens();
2662 return *std::max_element(
2663 screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) {
2664 const QSize left_size = left->geometry().intersected(window->geometry()).size();
2665 const QSize right_size = right->geometry().intersected(window->geometry()).size();
2666 return (left_size.height() * left_size.width()) <
2667 (right_size.height() * right_size.width());
2668 });
2669}
2670
2582void GMainWindow::ShowFullscreen() { 2671void GMainWindow::ShowFullscreen() {
2583 const auto show_fullscreen = [](QWidget* window) { 2672 const auto show_fullscreen = [](QWidget* window) {
2584 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { 2673 if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
@@ -2587,7 +2676,7 @@ void GMainWindow::ShowFullscreen() {
2587 } 2676 }
2588 window->hide(); 2677 window->hide();
2589 window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint); 2678 window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
2590 const auto screen_geometry = QApplication::desktop()->screenGeometry(window); 2679 const auto screen_geometry = GuessCurrentScreen(window)->geometry();
2591 window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(), 2680 window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
2592 screen_geometry.height() + 1); 2681 screen_geometry.height() + 1);
2593 window->raise(); 2682 window->raise();
@@ -2771,6 +2860,10 @@ void GMainWindow::OnConfigure() {
2771 mouse_hide_timer.start(); 2860 mouse_hide_timer.start();
2772 } 2861 }
2773 2862
2863 if (!UISettings::values.has_broken_vulkan) {
2864 renderer_status_button->setEnabled(!emulation_running);
2865 }
2866
2774 UpdateStatusButtons(); 2867 UpdateStatusButtons();
2775 controller_dialog->refreshConfiguration(); 2868 controller_dialog->refreshConfiguration();
2776} 2869}
@@ -2856,7 +2949,7 @@ void GMainWindow::OnToggleDockedMode() {
2856 } 2949 }
2857 2950
2858 Settings::values.use_docked_mode.SetValue(!is_docked); 2951 Settings::values.use_docked_mode.SetValue(!is_docked);
2859 dock_status_button->setChecked(!is_docked); 2952 UpdateDockedButton();
2860 OnDockedModeChanged(is_docked, !is_docked, *system); 2953 OnDockedModeChanged(is_docked, !is_docked, *system);
2861} 2954}
2862 2955
@@ -2895,7 +2988,7 @@ void GMainWindow::OnToggleAdaptingFilter() {
2895 2988
2896void GMainWindow::OnConfigurePerGame() { 2989void GMainWindow::OnConfigurePerGame() {
2897 const u64 title_id = system->GetCurrentProcessProgramID(); 2990 const u64 title_id = system->GetCurrentProcessProgramID();
2898 OpenPerGameConfiguration(title_id, game_path.toStdString()); 2991 OpenPerGameConfiguration(title_id, current_game_path.toStdString());
2899} 2992}
2900 2993
2901void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) { 2994void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
@@ -3150,7 +3243,7 @@ void GMainWindow::OnTasStateChanged() {
3150} 3243}
3151 3244
3152void GMainWindow::UpdateStatusBar() { 3245void GMainWindow::UpdateStatusBar() {
3153 if (emu_thread == nullptr) { 3246 if (emu_thread == nullptr || !system->IsPoweredOn()) {
3154 status_bar_update_timer.stop(); 3247 status_bar_update_timer.stop();
3155 return; 3248 return;
3156 } 3249 }
@@ -3222,6 +3315,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3222 } 3315 }
3223} 3316}
3224 3317
3318void GMainWindow::UpdateDockedButton() {
3319 const bool is_docked = Settings::values.use_docked_mode.GetValue();
3320 dock_status_button->setChecked(is_docked);
3321 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3322}
3323
3225void GMainWindow::UpdateFilterText() { 3324void GMainWindow::UpdateFilterText() {
3226 const auto filter = Settings::values.scaling_filter.GetValue(); 3325 const auto filter = Settings::values.scaling_filter.GetValue();
3227 switch (filter) { 3326 switch (filter) {
@@ -3265,10 +3364,10 @@ void GMainWindow::UpdateAAText() {
3265} 3364}
3266 3365
3267void GMainWindow::UpdateStatusButtons() { 3366void GMainWindow::UpdateStatusButtons() {
3268 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
3269 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3367 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3270 Settings::RendererBackend::Vulkan); 3368 Settings::RendererBackend::Vulkan);
3271 UpdateGPUAccuracyButton(); 3369 UpdateGPUAccuracyButton();
3370 UpdateDockedButton();
3272 UpdateFilterText(); 3371 UpdateFilterText();
3273 UpdateAAText(); 3372 UpdateAAText();
3274} 3373}
@@ -3319,7 +3418,7 @@ void GMainWindow::CenterMouseCursor() {
3319 const int center_x = render_window->width() / 2; 3418 const int center_x = render_window->width() / 2;
3320 const int center_y = render_window->height() / 2; 3419 const int center_y = render_window->height() / 2;
3321 3420
3322 QCursor::setPos(mapToGlobal({center_x, center_y})); 3421 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
3323} 3422}
3324 3423
3325void GMainWindow::OnMouseActivity() { 3424void GMainWindow::OnMouseActivity() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b399e9b01..8cf224c9c 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -320,6 +320,7 @@ private:
320 void MigrateConfigFiles(); 320 void MigrateConfigFiles();
321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
322 std::string_view gpu_vendor = {}); 322 std::string_view gpu_vendor = {});
323 void UpdateDockedButton();
323 void UpdateFilterText(); 324 void UpdateFilterText();
324 void UpdateAAText(); 325 void UpdateAAText();
325 void UpdateStatusBar(); 326 void UpdateStatusBar();
@@ -368,7 +369,7 @@ private:
368 bool emulation_running = false; 369 bool emulation_running = false;
369 std::unique_ptr<EmuThread> emu_thread; 370 std::unique_ptr<EmuThread> emu_thread;
370 // The path to the game currently running 371 // The path to the game currently running
371 QString game_path; 372 QString current_game_path;
372 373
373 bool auto_paused = false; 374 bool auto_paused = false;
374 bool auto_muted = false; 375 bool auto_muted = false;
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 15ba9ea17..c64d87ace 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -77,6 +77,8 @@ struct Values {
77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; 79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
80 // Set when Vulkan is known to crash the application
81 Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
80 82
81 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; 83 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
82 84
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index fc16f0f0c..fc4744fb0 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -344,6 +344,8 @@ void Config::ReadValues() {
344 ReadSetting("Debugging", Settings::values.use_debug_asserts); 344 ReadSetting("Debugging", Settings::values.use_debug_asserts);
345 ReadSetting("Debugging", Settings::values.use_auto_stub); 345 ReadSetting("Debugging", Settings::values.use_auto_stub);
346 ReadSetting("Debugging", Settings::values.disable_macro_jit); 346 ReadSetting("Debugging", Settings::values.disable_macro_jit);
347 ReadSetting("Debugging", Settings::values.use_gdbstub);
348 ReadSetting("Debugging", Settings::values.gdbstub_port);
347 349
348 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 350 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
349 std::stringstream ss(title_list); 351 std::stringstream ss(title_list);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f34d6b728..a3b8432f5 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor =
218 218
219[Renderer] 219[Renderer]
220# Which backend API to use. 220# Which backend API to use.
221# 0 (default): OpenGL, 1: Vulkan 221# 0: OpenGL, 1 (default): Vulkan
222backend = 222backend =
223 223
224# Enable graphics API debugging mode. 224# Enable graphics API debugging mode.
@@ -437,6 +437,11 @@ disable_macro_jit=false
437# Presents guest frames as they become available. Experimental. 437# Presents guest frames as they become available. Experimental.
438# false: Disabled (default), true: Enabled 438# false: Disabled (default), true: Enabled
439disable_fps_limit=false 439disable_fps_limit=false
440# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
441# false: Disabled (default), true: Enabled
442use_gdbstub=false
443# The port to use for the GDB server, if it is enabled.
444gdbstub_port=6543
440 445
441[WebService] 446[WebService]
442# Whether or not to enable telemetry 447# Whether or not to enable telemetry
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index ae2e62dc5..8e38724db 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -93,7 +93,7 @@ void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
93} 93}
94 94
95void EmuWindow_SDL2::OnFingerUp() { 95void EmuWindow_SDL2::OnFingerUp() {
96 input_subsystem->GetTouchScreen()->TouchReleased(0); 96 input_subsystem->GetTouchScreen()->ReleaseAllTouch();
97} 97}
98 98
99void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) { 99void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
@@ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() {
162 SDL_Event event; 162 SDL_Event event;
163 163
164 if (!SDL_WaitEvent(&event)) { 164 if (!SDL_WaitEvent(&event)) {
165 LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError()); 165 const char* error = SDL_GetError();
166 if (!error || strcmp(error, "") == 0) {
167 // https://github.com/libsdl-org/SDL/issues/5780
168 // Sometimes SDL will return without actually having hit an error condition;
169 // just ignore it in this case.
170 return;
171 }
172
173 LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
166 exit(1); 174 exit(1);
167 } 175 }
168 176
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 9746585f5..58b885465 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -20,7 +20,7 @@ enum class MouseButton;
20 20
21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { 21class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
22public: 22public:
23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_); 23 explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
24 ~EmuWindow_SDL2(); 24 ~EmuWindow_SDL2();
25 25
26 /// Whether the window is still open, and a close request hasn't yet been sent 26 /// Whether the window is still open, and a close request hasn't yet been sent
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index 8075c9082..9b660c13c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -73,9 +73,9 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
73 return unsupported_ext.empty(); 73 return unsupported_ext.empty();
74} 74}
75 75
76EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, 76EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_,
77 Core::System& system_, bool fullscreen) 77 Core::System& system_, bool fullscreen)
78 : EmuWindow_SDL2{input_subsystem, system_} { 78 : EmuWindow_SDL2{input_subsystem_, system_} {
79 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 79 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
80 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); 80 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
81 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); 81 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index d159166fd..39346e704 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -17,7 +17,7 @@ class InputSubsystem;
17 17
18class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { 18class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
19public: 19public:
20 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_, 20 explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_,
21 bool fullscreen); 21 bool fullscreen);
22 ~EmuWindow_SDL2_GL(); 22 ~EmuWindow_SDL2_GL();
23 23
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 d5fe35aa0..65455c86e 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -21,9 +21,9 @@
21#include <SDL.h> 21#include <SDL.h>
22#include <SDL_syswm.h> 22#include <SDL_syswm.h>
23 23
24EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, 24EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_,
25 Core::System& system_, bool fullscreen) 25 Core::System& system_, bool fullscreen)
26 : EmuWindow_SDL2{input_subsystem, system_} { 26 : EmuWindow_SDL2{input_subsystem_, system_} {
27 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, 27 const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
28 Common::g_scm_branch, Common::g_scm_desc); 28 Common::g_scm_branch, Common::g_scm_desc);
29 render_window = 29 render_window =
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index d92e3aaab..e39ad754d 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -18,7 +18,7 @@ class InputSubsystem;
18 18
19class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { 19class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
20public: 20public:
21 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system, 21 explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system,
22 bool fullscreen); 22 bool fullscreen);
23 ~EmuWindow_SDL2_VK() override; 23 ~EmuWindow_SDL2_VK() override;
24 24
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index ab12dd15d..0dce5e274 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -217,10 +217,19 @@ int main(int argc, char** argv) {
217 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); 217 [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
218 } 218 }
219 219
220 system.RegisterExitCallback([&] {
221 // Just exit right away.
222 exit(0);
223 });
224
220 void(system.Run()); 225 void(system.Run());
226 if (system.DebuggerEnabled()) {
227 system.InitializeDebugger();
228 }
221 while (emu_window->IsOpen()) { 229 while (emu_window->IsOpen()) {
222 emu_window->WaitEvent(); 230 emu_window->WaitEvent();
223 } 231 }
232 system.DetachDebugger();
224 void(system.Pause()); 233 void(system.Pause());
225 system.Shutdown(); 234 system.Shutdown();
226 235