summaryrefslogtreecommitdiffstats
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)
/we4305 # 'context': truncation from 'type1' to 'type2'
/we4388 # 'expression': signed/unsigned mismatch
/we4389 # 'operator': signed/unsigned mismatch
+ /we4456 # Declaration of 'identifier' hides previous local declaration
+ /we4457 # Declaration of 'identifier' hides function parameter
+ /we4458 # Declaration of 'identifier' hides class member
+ /we4459 # Declaration of 'identifier' hides global declaration
/we4505 # 'function': unreferenced local function has been removed
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
@@ -92,6 +96,7 @@ else()
-Werror=missing-declarations
-Werror=missing-field-initializers
-Werror=reorder
+ -Werror=shadow
-Werror=sign-compare
-Werror=switch
-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)
target_compile_options(audio_core PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
- -Werror=shadow
- -Werror=unused-parameter
- -Werror=unused-variable
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<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) {
return static_cast<s32>(rescaled_sample);
}
-constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
- 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
+constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
-constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
- 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
+constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
+ 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
-constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
- 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
+constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
+ 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
template <std::size_t CHANNEL_COUNT>
void ApplyReverbGeneric(
@@ -429,7 +429,7 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
in_params.node_id);
break;
default:
- UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
+ ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
}
}
}
@@ -1312,7 +1312,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::s
samples_to_read - samples_read, channel, temp_mix_offset);
break;
default:
- UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
+ ASSERT_MSG(false, "Unimplemented sample format={}", in_params.sample_format);
}
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) {
effects[i] = std::make_unique<EffectBiquadFilter>();
break;
default:
- UNREACHABLE_MSG("Unimplemented effect {}", effect);
+ ASSERT_MSG(false, "Unimplemented effect {}", effect);
effects[i] = std::make_unique<EffectStubbed>();
}
return GetInfo(i);
@@ -104,7 +104,7 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
auto& params = GetParams();
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
- UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
+ ASSERT_MSG(false, "Invalid reverb max channel count {}", reverb_params->max_channels);
return;
}
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) {
// Add more work
index_stack.push(j);
} else if (node_state == NodeStates::State::InFound) {
- UNREACHABLE_MSG("Node start marked as found");
+ ASSERT_MSG(false, "Node start marked as found");
ResetState();
return false;
}
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,
in_params.current_playstate = ServerPlayState::Play;
break;
default:
- UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
+ ASSERT_MSG(false, "Unknown playstate {}", voice_in.play_state);
break;
}
@@ -410,7 +410,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
return in_params.should_depop;
}
default:
- UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
+ ASSERT_MSG(false, "Invalid playstate {}", in_params.current_playstate);
}
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
div_ceil.h
dynamic_library.cpp
dynamic_library.h
+ elf.h
error.cpp
error.h
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 @@
#include "common/settings.h"
-void assert_handle_failure() {
+void assert_fail_impl() {
if (Settings::values.use_debug_asserts) {
Crash();
}
}
+
+[[noreturn]] void unreachable_impl() {
+ Crash();
+ throw std::runtime_error("Unreachable code");
+}
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 @@
// Sometimes we want to try to continue even after hitting an assert.
// However touching this file yields a global recompilation as this header is included almost
// everywhere. So let's just move the handling of the failed assert to a single cpp file.
-void assert_handle_failure();
-// For asserts we'd like to keep all the junk executed when an assert happens away from the
-// important code in the function. One way of doing this is to put all the relevant code inside a
-// lambda and force the compiler to not inline it. Unfortunately, MSVC seems to have no syntax to
-// specify __declspec on lambda functions, so what we do instead is define a noinline wrapper
-// template that calls the lambda. This seems to generate an extra instruction at the call-site
-// compared to the ideal implementation (which wouldn't support ASSERT_MSG parameters), but is good
-// enough for our purposes.
-template <typename Fn>
-#if defined(_MSC_VER)
-[[msvc::noinline]]
-#elif defined(__GNUC__)
-[[gnu::cold, gnu::noinline]]
+void assert_fail_impl();
+[[noreturn]] void unreachable_impl();
+
+#ifdef _MSC_VER
+#define YUZU_NO_INLINE __declspec(noinline)
+#else
+#define YUZU_NO_INLINE __attribute__((noinline))
#endif
-static void
-assert_noinline_call(const Fn& fn) {
- fn();
- assert_handle_failure();
-}
#define ASSERT(_a_) \
- do \
- if (!(_a_)) { \
- assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
+ ([&]() YUZU_NO_INLINE { \
+ if (!(_a_)) [[unlikely]] { \
+ LOG_CRITICAL(Debug, "Assertion Failed!"); \
+ assert_fail_impl(); \
} \
- while (0)
+ }())
#define ASSERT_MSG(_a_, ...) \
- do \
- if (!(_a_)) { \
- assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
+ ([&]() YUZU_NO_INLINE { \
+ if (!(_a_)) [[unlikely]] { \
+ LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); \
+ assert_fail_impl(); \
} \
- while (0)
+ }())
+
+#define UNREACHABLE() \
+ do { \
+ LOG_CRITICAL(Debug, "Unreachable code!"); \
+ unreachable_impl(); \
+ } while (0)
-#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
#define UNREACHABLE_MSG(...) \
- assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
+ do { \
+ LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); \
+ unreachable_impl(); \
+ } while (0)
#ifdef _DEBUG
#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 @@
+// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
+// SPDX-License-Identifier: MIT
+#pragma once
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4324)
+#endif
+
+#include <atomic>
+#include <bit>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <new>
+#include <stdexcept>
+#include <stop_token>
+#include <type_traits>
+#include <utility>
+
+namespace Common {
+namespace mpsc {
+#if defined(__cpp_lib_hardware_interference_size)
+constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
+#else
+constexpr size_t hardware_interference_size = 64;
+#endif
+
+template <typename T>
+using AlignedAllocator = std::allocator<T>;
+
+template <typename T>
+struct Slot {
+ ~Slot() noexcept {
+ if (turn.test()) {
+ destroy();
+ }
+ }
+
+ template <typename... Args>
+ void construct(Args&&... args) noexcept {
+ static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
+ "T must be nothrow constructible with Args&&...");
+ std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...);
+ }
+
+ void destroy() noexcept {
+ static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
+ std::destroy_at(reinterpret_cast<T*>(&storage));
+ }
+
+ T&& move() noexcept {
+ return reinterpret_cast<T&&>(storage);
+ }
+
+ // Align to avoid false sharing between adjacent slots
+ alignas(hardware_interference_size) std::atomic_flag turn{};
+ struct aligned_store {
+ struct type {
+ alignas(T) unsigned char data[sizeof(T)];
+ };
+ };
+ typename aligned_store::type storage;
+};
+
+template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
+class Queue {
+public:
+ explicit Queue(const size_t capacity, const Allocator& allocator = Allocator())
+ : allocator_(allocator) {
+ if (capacity < 1) {
+ throw std::invalid_argument("capacity < 1");
+ }
+ // Ensure that the queue length is an integer power of 2
+ // This is so that idx(i) can be a simple i & mask_ insted of i % capacity
+ // https://github.com/rigtorp/MPMCQueue/pull/36
+ if (!std::has_single_bit(capacity)) {
+ throw std::invalid_argument("capacity must be an integer power of 2");
+ }
+
+ mask_ = capacity - 1;
+
+ // Allocate one extra slot to prevent false sharing on the last slot
+ slots_ = allocator_.allocate(mask_ + 2);
+ // Allocators are not required to honor alignment for over-aligned types
+ // (see http://eel.is/c++draft/allocator.requirements#10) so we verify
+ // alignment here
+ if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) {
+ allocator_.deallocate(slots_, mask_ + 2);
+ throw std::bad_alloc();
+ }
+ for (size_t i = 0; i < mask_ + 1; ++i) {
+ std::construct_at(&slots_[i]);
+ }
+ static_assert(alignof(Slot<T>) == hardware_interference_size,
+ "Slot must be aligned to cache line boundary to prevent false sharing");
+ static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
+ "Slot size must be a multiple of cache line size to prevent "
+ "false sharing between adjacent slots");
+ static_assert(sizeof(Queue) % hardware_interference_size == 0,
+ "Queue size must be a multiple of cache line size to "
+ "prevent false sharing between adjacent queues");
+ }
+
+ ~Queue() noexcept {
+ for (size_t i = 0; i < mask_ + 1; ++i) {
+ slots_[i].~Slot();
+ }
+ allocator_.deallocate(slots_, mask_ + 2);
+ }
+
+ // non-copyable and non-movable
+ Queue(const Queue&) = delete;
+ Queue& operator=(const Queue&) = delete;
+
+ void Push(const T& v) noexcept {
+ static_assert(std::is_nothrow_copy_constructible_v<T>,
+ "T must be nothrow copy constructible");
+ emplace(v);
+ }
+
+ template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
+ void Push(P&& v) noexcept {
+ emplace(std::forward<P>(v));
+ }
+
+ void Pop(T& v, std::stop_token stop) noexcept {
+ auto const tail = tail_.fetch_add(1);
+ auto& slot = slots_[idx(tail)];
+ if (false == slot.turn.test()) {
+ std::unique_lock lock{cv_mutex};
+ cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
+ }
+ v = slot.move();
+ slot.destroy();
+ slot.turn.clear();
+ slot.turn.notify_one();
+ }
+
+private:
+ template <typename... Args>
+ void emplace(Args&&... args) noexcept {
+ static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
+ "T must be nothrow constructible with Args&&...");
+ auto const head = head_.fetch_add(1);
+ auto& slot = slots_[idx(head)];
+ slot.turn.wait(true);
+ slot.construct(std::forward<Args>(args)...);
+ slot.turn.test_and_set();
+ cv.notify_one();
+ }
+
+ constexpr size_t idx(size_t i) const noexcept {
+ return i & mask_;
+ }
+
+ std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv;
+ std::mutex cv_mutex;
+ size_t mask_;
+ Slot<T>* slots_;
+ [[no_unique_address]] Allocator allocator_;
+
+ // Align to avoid false sharing between head_ and tail_
+ alignas(hardware_interference_size) std::atomic<size_t> head_{0};
+ alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
+
+ static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
+ "T must be nothrow copy or move assignable");
+
+ static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
+};
+} // namespace mpsc
+
+template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>>
+using MPSCQueue = mpsc::Queue<T, Allocator>;
+
+} // namespace Common
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#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) {
++instance->count;
std::thread([task{std::move(task)}]() {
task();
- std::unique_lock lock{instance->mutex};
+ std::unique_lock thread_lock{instance->mutex};
--instance->count;
- std::notify_all_at_thread_exit(instance->cv, std::move(lock));
+ std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock));
}).detach();
}
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 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+
+#include "common_types.h"
+
+namespace Common {
+namespace ELF {
+
+/* Type for a 16-bit quantity. */
+using Elf32_Half = u16;
+using Elf64_Half = u16;
+
+/* Types for signed and unsigned 32-bit quantities. */
+using Elf32_Word = u32;
+using Elf32_Sword = s32;
+using Elf64_Word = u32;
+using Elf64_Sword = s32;
+
+/* Types for signed and unsigned 64-bit quantities. */
+using Elf32_Xword = u64;
+using Elf32_Sxword = s64;
+using Elf64_Xword = u64;
+using Elf64_Sxword = s64;
+
+/* Type of addresses. */
+using Elf32_Addr = u32;
+using Elf64_Addr = u64;
+
+/* Type of file offsets. */
+using Elf32_Off = u32;
+using Elf64_Off = u64;
+
+/* Type for section indices, which are 16-bit quantities. */
+using Elf32_Section = u16;
+using Elf64_Section = u16;
+
+/* Type for version symbol information. */
+using Elf32_Versym = Elf32_Half;
+using Elf64_Versym = Elf64_Half;
+
+constexpr size_t ElfIdentSize = 16;
+
+/* The ELF file header. This appears at the start of every ELF file. */
+
+struct Elf32_Ehdr {
+ std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
+ Elf32_Half e_type; /* Object file type */
+ Elf32_Half e_machine; /* Architecture */
+ Elf32_Word e_version; /* Object file version */
+ Elf32_Addr e_entry; /* Entry point virtual address */
+ Elf32_Off e_phoff; /* Program header table file offset */
+ Elf32_Off e_shoff; /* Section header table file offset */
+ Elf32_Word e_flags; /* Processor-specific flags */
+ Elf32_Half e_ehsize; /* ELF header size in bytes */
+ Elf32_Half e_phentsize; /* Program header table entry size */
+ Elf32_Half e_phnum; /* Program header table entry count */
+ Elf32_Half e_shentsize; /* Section header table entry size */
+ Elf32_Half e_shnum; /* Section header table entry count */
+ Elf32_Half e_shstrndx; /* Section header string table index */
+};
+
+struct Elf64_Ehdr {
+ std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
+ Elf64_Half e_type; /* Object file type */
+ Elf64_Half e_machine; /* Architecture */
+ Elf64_Word e_version; /* Object file version */
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags; /* Processor-specific flags */
+ Elf64_Half e_ehsize; /* ELF header size in bytes */
+ Elf64_Half e_phentsize; /* Program header table entry size */
+ Elf64_Half e_phnum; /* Program header table entry count */
+ Elf64_Half e_shentsize; /* Section header table entry size */
+ Elf64_Half e_shnum; /* Section header table entry count */
+ Elf64_Half e_shstrndx; /* Section header string table index */
+};
+
+constexpr u8 ElfClass32 = 1; /* 32-bit objects */
+constexpr u8 ElfClass64 = 2; /* 64-bit objects */
+constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
+constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
+constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
+
+constexpr u16 ElfTypeNone = 0; /* No file type */
+constexpr u16 ElfTypeRel = 0; /* Relocatable file */
+constexpr u16 ElfTypeExec = 0; /* Executable file */
+constexpr u16 ElfTypeDyn = 0; /* Shared object file */
+
+constexpr u16 ElfMachineArm = 40; /* ARM */
+constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
+
+constexpr std::array<u8, ElfIdentSize> Elf32Ident{
+ 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
+
+constexpr std::array<u8, ElfIdentSize> Elf64Ident{
+ 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
+
+/* Section header. */
+
+struct Elf32_Shdr {
+ Elf32_Word sh_name; /* Section name (string tbl index) */
+ Elf32_Word sh_type; /* Section type */
+ Elf32_Word sh_flags; /* Section flags */
+ Elf32_Addr sh_addr; /* Section virtual addr at execution */
+ Elf32_Off sh_offset; /* Section file offset */
+ Elf32_Word sh_size; /* Section size in bytes */
+ Elf32_Word sh_link; /* Link to another section */
+ Elf32_Word sh_info; /* Additional section information */
+ Elf32_Word sh_addralign; /* Section alignment */
+ Elf32_Word sh_entsize; /* Entry size if section holds table */
+};
+
+struct Elf64_Shdr {
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+};
+
+constexpr u32 ElfShnUndef = 0; /* Undefined section */
+
+constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
+constexpr u32 ElfShtProgBits = 1; /* Program data */
+constexpr u32 ElfShtSymtab = 2; /* Symbol table */
+constexpr u32 ElfShtStrtab = 3; /* String table */
+constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
+constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
+constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
+constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
+constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
+
+/* Symbol table entry. */
+
+struct Elf32_Sym {
+ Elf32_Word st_name; /* Symbol name (string tbl index) */
+ Elf32_Addr st_value; /* Symbol value */
+ Elf32_Word st_size; /* Symbol size */
+ u8 st_info; /* Symbol type and binding */
+ u8 st_other; /* Symbol visibility */
+ Elf32_Section st_shndx; /* Section index */
+};
+
+struct Elf64_Sym {
+ Elf64_Word st_name; /* Symbol name (string tbl index) */
+ u8 st_info; /* Symbol type and binding */
+ u8 st_other; /* Symbol visibility */
+ Elf64_Section st_shndx; /* Section index */
+ Elf64_Addr st_value; /* Symbol value */
+ Elf64_Xword st_size; /* Symbol size */
+};
+
+/* How to extract and insert information held in the st_info field. */
+
+static inline u8 ElfStBind(u8 st_info) {
+ return st_info >> 4;
+}
+static inline u8 ElfStType(u8 st_info) {
+ return st_info & 0xf;
+}
+static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
+ return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
+}
+
+constexpr u8 ElfBindLocal = 0; /* Local symbol */
+constexpr u8 ElfBindGlobal = 1; /* Global symbol */
+constexpr u8 ElfBindWeak = 2; /* Weak symbol */
+
+constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
+constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
+constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
+
+static inline u8 ElfStVisibility(u8 st_other) {
+ return static_cast<u8>(st_other & 0x3);
+}
+
+constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
+constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
+constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
+constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
+
+/* Relocation table entry without addend (in section of type ShtRel). */
+
+struct Elf32_Rel {
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+};
+
+/* Relocation table entry with addend (in section of type ShtRela). */
+
+struct Elf32_Rela {
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+ Elf32_Sword r_addend; /* Addend */
+};
+
+struct Elf64_Rela {
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+ Elf64_Sxword r_addend; /* Addend */
+};
+
+/* How to extract and insert information held in the r_info field. */
+
+static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
+ return r_info >> 8;
+}
+static inline u8 Elf32RelType(Elf32_Word r_info) {
+ return static_cast<u8>(r_info & 0xff);
+}
+static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
+ return (sym_index << 8) + type;
+}
+static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
+ return static_cast<u32>(r_info >> 32);
+}
+static inline u32 Elf64RelType(Elf64_Xword r_info) {
+ return r_info & 0xffffffff;
+}
+static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
+ return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
+}
+
+constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
+constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
+constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
+constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
+
+constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
+constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
+constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
+constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
+
+/* Program segment header. */
+
+struct Elf32_Phdr {
+ Elf32_Word p_type; /* Segment type */
+ Elf32_Off p_offset; /* Segment file offset */
+ Elf32_Addr p_vaddr; /* Segment virtual address */
+ Elf32_Addr p_paddr; /* Segment physical address */
+ Elf32_Word p_filesz; /* Segment size in file */
+ Elf32_Word p_memsz; /* Segment size in memory */
+ Elf32_Word p_flags; /* Segment flags */
+ Elf32_Word p_align; /* Segment alignment */
+};
+
+struct Elf64_Phdr {
+ Elf64_Word p_type; /* Segment type */
+ Elf64_Word p_flags; /* Segment flags */
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment */
+};
+
+/* Legal values for p_type (segment type). */
+
+constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
+constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
+constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
+constexpr u32 ElfPtInterp = 3; /* Program interpreter */
+constexpr u32 ElfPtNote = 4; /* Auxiliary information */
+constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
+constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
+
+/* Legal values for p_flags (segment flags). */
+
+constexpr u32 ElfPfExec = 0; /* Segment is executable */
+constexpr u32 ElfPfWrite = 1; /* Segment is writable */
+constexpr u32 ElfPfRead = 2; /* Segment is readable */
+
+/* Dynamic section entry. */
+
+struct Elf32_Dyn {
+ Elf32_Sword d_tag; /* Dynamic entry type */
+ union {
+ Elf32_Word d_val; /* Integer value */
+ Elf32_Addr d_ptr; /* Address value */
+ } d_un;
+};
+
+struct Elf64_Dyn {
+ Elf64_Sxword d_tag; /* Dynamic entry type */
+ union {
+ Elf64_Xword d_val; /* Integer value */
+ Elf64_Addr d_ptr; /* Address value */
+ } d_un;
+};
+
+/* Legal values for d_tag (dynamic entry type). */
+
+constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
+constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
+constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
+constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
+constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
+constexpr u32 ElfDtStrtab = 5; /* Address of string table */
+constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
+constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
+constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
+constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
+constexpr u32 ElfDtStrsz = 10; /* Size of string table */
+constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
+constexpr u32 ElfDtInit = 12; /* Address of init function */
+constexpr u32 ElfDtFini = 13; /* Address of termination function */
+constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
+constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
+constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
+constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
+constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
+constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
+constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
+constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
+constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
+constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
+constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
+constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
+
+} // namespace ELF
+} // 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) {
fs::path GetExeDirectory() {
wchar_t exe_path[MAX_PATH];
- GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
-
- if (!exe_path) {
+ if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
LOG_ERROR(Common_Filesystem,
"Failed to get the path to the executable of the current process");
}
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 {
enum class VibrationAmplificationType {
Linear,
Exponential,
+ Test,
};
// 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() {
log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
+ log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
@@ -146,7 +147,7 @@ void UpdateRescalingInfo() {
info.down_shift = 0;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
info.up_scale = 1;
info.down_shift = 0;
}
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 {
// Renderer
RangedSetting<RendererBackend> renderer_backend{
- RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
+ RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
BasicSetting<bool> renderer_debug{false, "debug"};
BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -601,11 +601,12 @@ struct Values {
// Debugging
bool record_frame_times;
BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
- BasicSetting<u16> gdbstub_port{0, "gdbstub_port"};
+ BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
BasicSetting<std::string> program_args{std::string(), "program_args"};
BasicSetting<bool> dump_exefs{false, "dump_exefs"};
BasicSetting<bool> dump_nso{false, "dump_nso"};
BasicSetting<bool> dump_shaders{false, "dump_shaders"};
+ BasicSetting<bool> dump_macros{false, "dump_macros"};
BasicSetting<bool> enable_fs_access_log{false, "enable_fs_access_log"};
BasicSetting<bool> reporting_services{false, "reporting_services"};
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) {
#endif
+std::u16string U16StringFromBuffer(const u16* input, std::size_t length) {
+ return std::u16string(reinterpret_cast<const char16_t*>(input), length);
+}
+
std::string StringFromFixedZeroTerminatedBuffer(std::string_view buffer, std::size_t max_len) {
std::size_t len = 0;
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* _
#endif
+[[nodiscard]] std::u16string U16StringFromBuffer(const u16* input, std::size_t length);
+
/**
* Compares the string defined by the range [`begin`, `end`) to the null-terminated C-string
* `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
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
+ debugger/debugger_interface.h
+ debugger/debugger.cpp
+ debugger/debugger.h
+ debugger/gdbstub_arch.cpp
+ debugger/gdbstub_arch.h
+ debugger/gdbstub.cpp
+ debugger/gdbstub.h
device_memory.cpp
device_memory.h
file_sys/bis_factory.cpp
@@ -736,16 +743,11 @@ if (MSVC)
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
- /we4456 # Declaration of 'identifier' hides previous local declaration
- /we4457 # Declaration of 'identifier' hides function parameter
- /we4458 # Declaration of 'identifier' hides class member
- /we4459 # Declaration of 'identifier' hides global declaration
)
else()
target_compile_options(core PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
- -Werror=shadow
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
@@ -761,6 +763,9 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
+if (MINGW)
+ target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
+endif()
if (ENABLE_WEB_SERVICE)
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 @@
#include "core/arm/arm_interface.h"
#include "core/arm/symbols.h"
#include "core/core.h"
+#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -88,4 +90,50 @@ void ARM_Interface::LogBacktrace() const {
}
}
+void ARM_Interface::Run() {
+ using Kernel::StepState;
+ using Kernel::SuspendType;
+
+ while (true) {
+ Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
+ Dynarmic::HaltReason hr{};
+
+ // Notify the debugger and go to sleep if a step was performed
+ // and this thread has been scheduled again.
+ if (current_thread->GetStepState() == StepState::StepPerformed) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ current_thread->RequestSuspend(SuspendType::Debug);
+ break;
+ }
+
+ // Otherwise, run the thread.
+ system.EnterDynarmicProfile();
+ if (current_thread->GetStepState() == StepState::StepPending) {
+ hr = StepJit();
+
+ if (Has(hr, step_thread)) {
+ current_thread->SetStepState(StepState::StepPerformed);
+ }
+ } else {
+ hr = RunJit();
+ }
+ system.ExitDynarmicProfile();
+
+ // Notify the debugger and go to sleep if a breakpoint was hit.
+ if (Has(hr, breakpoint)) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ current_thread->RequestSuspend(Kernel::SuspendType::Debug);
+ break;
+ }
+
+ // Handle syscalls and scheduling (this may change the current thread)
+ if (Has(hr, svc_call)) {
+ Kernel::Svc::Call(system, GetSvcNumber());
+ }
+ if (Has(hr, break_loop) || !uses_wall_clock) {
+ break;
+ }
+ }
+}
+
} // 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 @@
#include <array>
#include <vector>
+
+#include <dynarmic/interface/halt_reason.h>
+
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -64,10 +67,7 @@ public:
static_assert(sizeof(ThreadContext64) == 0x320);
/// Runs the CPU until an event happens
- virtual void Run() = 0;
-
- /// Step CPU by one instruction
- virtual void Step() = 0;
+ void Run();
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -194,6 +194,11 @@ public:
void LogBacktrace() const;
+ static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
+ static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
+ static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
+ static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
+
protected:
/// System context that this ARM interface is running under.
System& system;
@@ -201,6 +206,10 @@ protected:
bool uses_wall_clock;
static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
+
+ virtual Dynarmic::HaltReason RunJit() = 0;
+ virtual Dynarmic::HaltReason StepJit() = 0;
+ virtual u32 GetSvcNumber() const = 0;
};
} // 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 @@
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
@@ -24,9 +26,6 @@ namespace Core {
using namespace Common::Literals;
-constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
-constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
-
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -78,16 +77,21 @@ public:
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
+ if (parent.system.DebuggerEnabled()) {
+ parent.jit.load()->Regs()[15] = pc;
+ parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
+ return;
+ }
+
parent.LogBacktrace();
LOG_CRITICAL(Core_ARM,
"ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
- UNIMPLEMENTED();
}
void CallSVC(u32 swi) override {
parent.svc_swi = swi;
- parent.jit.load()->HaltExecution(svc_call);
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -232,20 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
return std::make_unique<Dynarmic::A32::Jit>(config);
}
-void ARM_Dynarmic_32::Run() {
- while (true) {
- const auto hr = jit.load()->Run();
- if (Has(hr, svc_call)) {
- Kernel::Svc::Call(system, svc_swi);
- }
- if (Has(hr, break_loop) || !uses_wall_clock) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
+ return jit.load()->Step();
}
-void ARM_Dynarmic_32::Step() {
- jit.load()->Step();
+u32 ARM_Dynarmic_32::GetSvcNumber() const {
+ return svc_swi;
}
ARM_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:
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
- void Run() override;
- void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -70,6 +68,11 @@ public:
std::vector<BacktraceEntry> GetBacktrace() const override;
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
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 @@
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -25,9 +26,6 @@ namespace Core {
using Vector = Dynarmic::A64::Vector;
using namespace Common::Literals;
-constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
-constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
-
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -119,8 +117,13 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
- case Dynarmic::A64::Exception::Breakpoint:
default:
+ if (parent.system.DebuggerEnabled()) {
+ parent.jit.load()->SetPC(pc);
+ parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
+ return;
+ }
+
parent.LogBacktrace();
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
@@ -129,7 +132,7 @@ public:
void CallSVC(u32 swi) override {
parent.svc_swi = swi;
- parent.jit.load()->HaltExecution(svc_call);
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -293,20 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
return std::make_shared<Dynarmic::A64::Jit>(config);
}
-void ARM_Dynarmic_64::Run() {
- while (true) {
- const auto hr = jit.load()->Run();
- if (Has(hr, svc_call)) {
- Kernel::Svc::Call(system, svc_swi);
- }
- if (Has(hr, break_loop) || !uses_wall_clock) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
+ return jit.load()->Step();
}
-void ARM_Dynarmic_64::Step() {
- jit.load()->Step();
+u32 ARM_Dynarmic_64::GetSvcNumber() const {
+ return svc_swi;
}
ARM_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:
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
- void Run() override;
- void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -64,6 +62,11 @@ public:
std::vector<BacktraceEntry> GetBacktrace() const override;
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
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> {
}
template <typename FormatContext>
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
- return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
+ return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
}
};
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 @@
#include "common/bit_field.h"
#include "common/common_funcs.h"
+#include "common/elf.h"
#include "core/arm/symbols.h"
#include "core/core.h"
#include "core/memory.h"
-namespace Core {
-namespace {
-
-constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
-constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
-constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
-constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
-
-enum class ELFSymbolType : u8 {
- None = 0,
- Object = 1,
- Function = 2,
- Section = 3,
- File = 4,
- Common = 5,
- TLS = 6,
-};
-
-enum class ELFSymbolBinding : u8 {
- Local = 0,
- Global = 1,
- Weak = 2,
-};
-
-enum class ELFSymbolVisibility : u8 {
- Default = 0,
- Internal = 1,
- Hidden = 2,
- Protected = 3,
-};
-
-struct ELF64Symbol {
- u32 name_index;
- union {
- u8 info;
-
- BitField<0, 4, ELFSymbolType> type;
- BitField<4, 4, ELFSymbolBinding> binding;
- };
- ELFSymbolVisibility visibility;
- u16 sh_index;
- u64 value;
- u64 size;
-};
-static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
-
-struct ELF32Symbol {
- u32 name_index;
- u32 value;
- u32 size;
- union {
- u8 info;
-
- BitField<0, 4, ELFSymbolType> type;
- BitField<4, 4, ELFSymbolBinding> binding;
- };
- ELFSymbolVisibility visibility;
- u16 sh_index;
-};
-static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
-
-} // Anonymous namespace
+using namespace Common::ELF;
+namespace Core {
namespace Symbols {
template <typename Word, typename ELFSymbol, typename ByteReader>
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
const Word value = ReadWord(dynamic_index + sizeof(Word));
dynamic_index += 2 * sizeof(Word);
- if (tag == ELF_DYNAMIC_TAG_NULL) {
+ if (tag == ElfDtNull) {
break;
}
- if (tag == ELF_DYNAMIC_TAG_STRTAB) {
+ if (tag == ElfDtStrtab) {
string_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
+ } else if (tag == ElfDtSymtab) {
symbol_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
+ } else if (tag == ElfDtSyment) {
symbol_entry_size = value;
}
}
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
ELFSymbol symbol{};
ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
- VAddr string_offset = string_table_offset + symbol.name_index;
+ VAddr string_offset = string_table_offset + symbol.st_name;
std::string name;
for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
name += static_cast<char>(c);
}
symbol_index += symbol_entry_size;
- out[name] = std::make_pair(symbol.value, symbol.size);
+ out[name] = std::make_pair(symbol.st_value, symbol.st_size);
}
return out;
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
[&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
if (is_64) {
- return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
- return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
}
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
}};
if (is_64) {
- return GetSymbols<u64, ELF64Symbol>(ReadBytes);
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
} else {
- return GetSymbols<u32, ELF32Symbol>(ReadBytes);
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
}
}
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 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
+#include "core/debugger/debugger.h"
#include "core/device_memory.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
@@ -171,6 +172,10 @@ struct System::Impl {
}
}
+ void InitializeDebugger(System& system, u16 port) {
+ debugger = std::make_unique<Debugger>(system, port);
+ }
+
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(Core, "initialized OK");
@@ -329,6 +334,7 @@ struct System::Impl {
gpu_core->NotifyShutdown();
}
+ debugger.reset();
services.reset();
service_manager.reset();
cheat_engine.reset();
@@ -436,6 +442,9 @@ struct System::Impl {
/// Network instance
Network::NetworkInstance network_instance;
+ /// Debugger
+ std::unique_ptr<Core::Debugger> debugger;
+
SystemResultStatus status = SystemResultStatus::Success;
std::string status_details = "";
@@ -472,10 +481,6 @@ SystemResultStatus System::Pause() {
return impl->Pause();
}
-SystemResultStatus System::SingleStep() {
- return SystemResultStatus::Success;
-}
-
void System::InvalidateCpuInstructionCaches() {
impl->kernel.InvalidateAllInstructionCaches();
}
@@ -488,6 +493,12 @@ void System::Shutdown() {
impl->Shutdown();
}
+void System::DetachDebugger() {
+ if (impl->debugger) {
+ impl->debugger->NotifyShutdown();
+ }
+}
+
std::unique_lock<std::mutex> System::StallCPU() {
return impl->StallCPU();
}
@@ -496,6 +507,10 @@ void System::UnstallCPU() {
impl->UnstallCPU();
}
+void System::InitializeDebugger() {
+ impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
+}
+
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
u64 program_id, std::size_t program_index) {
return impl->Load(*this, emu_window, filepath, program_id, program_index);
@@ -809,6 +824,18 @@ bool System::IsMulticore() const {
return impl->is_multicore;
}
+bool System::DebuggerEnabled() const {
+ return Settings::values.use_gdbstub.GetValue();
+}
+
+Core::Debugger& System::GetDebugger() {
+ return *impl->debugger;
+}
+
+const Core::Debugger& System::GetDebugger() const {
+ return *impl->debugger;
+}
+
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}
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 {
class ARM_Interface;
class CpuManager;
+class Debugger;
class DeviceMemory;
class ExclusiveMonitor;
class SpeedLimiter;
@@ -148,12 +149,6 @@ public:
[[nodiscard]] SystemResultStatus Pause();
/**
- * Step the CPU one instruction
- * @return Result status, indicating whether or not the operation succeeded.
- */
- [[nodiscard]] SystemResultStatus SingleStep();
-
- /**
* Invalidate the CPU instruction caches
* This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands.
@@ -165,10 +160,18 @@ public:
/// Shutdown the emulated system.
void Shutdown();
+ /// Forcibly detach the debugger if it is running.
+ void DetachDebugger();
+
std::unique_lock<std::mutex> StallCPU();
void UnstallCPU();
/**
+ * Initialize the debugger.
+ */
+ void InitializeDebugger();
+
+ /**
* Load an executable application.
* @param emu_window Reference to the host-system window used for video output and keyboard
* input.
@@ -354,6 +357,9 @@ public:
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
+ [[nodiscard]] Core::Debugger& GetDebugger();
+ [[nodiscard]] const Core::Debugger& GetDebugger() const;
+
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
@@ -375,6 +381,9 @@ public:
/// Tells if system is running on multicore.
[[nodiscard]] bool IsMulticore() const;
+ /// Tells if the system debugger is enabled.
+ [[nodiscard]] bool DebuggerEnabled() const;
+
/// Type used for the frontend to designate a callback for System to re-launch the application
/// using a specified program index.
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 @@
namespace Core {
-CpuManager::CpuManager(System& system_) : system{system_} {}
+CpuManager::CpuManager(System& system_)
+ : pause_barrier{std::make_unique<Common::Barrier>(1)}, system{system_} {}
CpuManager::~CpuManager() = default;
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
@@ -30,8 +31,10 @@ void CpuManager::Initialize() {
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
}
+ pause_barrier = std::make_unique<Common::Barrier>(Core::Hardware::NUM_CPU_CORES + 1);
} else {
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
+ pause_barrier = std::make_unique<Common::Barrier>(2);
}
}
@@ -110,12 +113,10 @@ void CpuManager::MultiCoreRunGuestLoop() {
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
- system.EnterDynarmicProfile();
while (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- system.ExitDynarmicProfile();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
@@ -138,51 +139,14 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto core = kernel.CurrentPhysicalCoreIndex();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
+ current_thread->DisableDispatch();
+
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
- ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
scheduler.RescheduleCurrentCore();
}
}
-void CpuManager::MultiCorePause(bool paused) {
- if (!paused) {
- bool all_not_barrier = false;
- while (!all_not_barrier) {
- all_not_barrier = true;
- for (const auto& data : core_data) {
- all_not_barrier &= !data.is_running.load() && data.initialized.load();
- }
- }
- for (auto& data : core_data) {
- data.enter_barrier->Set();
- }
- if (paused_state.load()) {
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = true;
- for (const auto& data : core_data) {
- all_barrier &= data.is_paused.load() && data.initialized.load();
- }
- }
- for (auto& data : core_data) {
- data.exit_barrier->Set();
- }
- }
- } else {
- /// Wait until all cores are paused.
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = true;
- for (const auto& data : core_data) {
- all_barrier &= data.is_paused.load() && data.initialized.load();
- }
- }
- /// Don't release the barrier
- }
- paused_state = paused;
-}
-
///////////////////////////////////////////////////////////////////////////////
/// SingleCore ///
///////////////////////////////////////////////////////////////////////////////
@@ -200,12 +164,10 @@ void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
- system.EnterDynarmicProfile();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- system.ExitDynarmicProfile();
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
@@ -235,8 +197,9 @@ void CpuManager::SingleCoreRunSuspendThread() {
auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
+ current_thread->DisableDispatch();
+
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
- ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
scheduler.RescheduleCurrentCore();
}
@@ -274,37 +237,21 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
}
}
-void CpuManager::SingleCorePause(bool paused) {
- if (!paused) {
- bool all_not_barrier = false;
- while (!all_not_barrier) {
- all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
- }
- core_data[0].enter_barrier->Set();
- if (paused_state.load()) {
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
- }
- core_data[0].exit_barrier->Set();
- }
- } else {
- /// Wait until all cores are paused.
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
- }
- /// Don't release the barrier
- }
- paused_state = paused;
-}
-
void CpuManager::Pause(bool paused) {
- if (is_multicore) {
- MultiCorePause(paused);
- } else {
- SingleCorePause(paused);
+ std::scoped_lock lk{pause_lock};
+
+ if (pause_state == paused) {
+ return;
}
+
+ // Set the new state
+ pause_state.store(paused);
+
+ // Wake up any waiting threads
+ pause_state.notify_all();
+
+ // Wait for all threads to successfully change state before returning
+ pause_barrier->Sync();
}
void 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) {
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
auto& data = core_data[core];
- data.enter_barrier = std::make_unique<Common::Event>();
- data.exit_barrier = std::make_unique<Common::Event>();
data.host_context = Common::Fiber::ThreadToFiber();
- data.is_running = false;
- data.initialized = true;
const bool sc_sync = !is_async_gpu && !is_multicore;
bool sc_sync_first_use = sc_sync;
// Cleanup
SCOPE_EXIT({
data.host_context->Exit();
- data.enter_barrier.reset();
- data.exit_barrier.reset();
- data.initialized = false;
MicroProfileOnThreadExit();
});
/// Running
while (running_mode) {
- data.is_running = false;
- data.enter_barrier->Wait();
+ if (pause_state.load(std::memory_order_relaxed)) {
+ // Wait for caller to acknowledge pausing
+ pause_barrier->Sync();
+
+ // Wait until unpaused
+ pause_state.wait(true, std::memory_order_relaxed);
+
+ // Wait for caller to acknowledge unpausing
+ pause_barrier->Sync();
+ }
+
if (sc_sync_first_use) {
system.GPU().ObtainContext();
sc_sync_first_use = false;
@@ -352,12 +301,7 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
}
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
- data.is_running = true;
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
- data.is_running = false;
- data.is_paused = true;
- data.exit_barrier->Wait();
- data.is_paused = false;
}
}
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:
void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
void MultiCoreRunSuspendThread();
- void MultiCorePause(bool paused);
void SingleCoreRunGuestThread();
void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
void SingleCoreRunSuspendThread();
- void SingleCorePause(bool paused);
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
@@ -83,16 +81,13 @@ private:
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
- std::unique_ptr<Common::Event> enter_barrier;
- std::unique_ptr<Common::Event> exit_barrier;
- std::atomic<bool> is_running;
- std::atomic<bool> is_paused;
- std::atomic<bool> initialized;
std::jthread host_thread;
};
std::atomic<bool> running_mode{};
- std::atomic<bool> paused_state{};
+ std::atomic<bool> pause_state{};
+ std::unique_ptr<Common::Barrier> pause_barrier{};
+ std::mutex pause_lock{};
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
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) {
return 0x3C;
}
UNREACHABLE();
- return 0;
}
u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -155,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
return 0x40;
}
UNREACHABLE();
- return 0;
}
SignatureType 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <mutex>
+#include <thread>
+
+#include <boost/asio.hpp>
+#include <boost/process/async_pipe.hpp>
+
+#include "common/logging/log.h"
+#include "common/thread.h"
+#include "core/core.h"
+#include "core/debugger/debugger.h"
+#include "core/debugger/debugger_interface.h"
+#include "core/debugger/gdbstub.h"
+#include "core/hle/kernel/global_scheduler_context.h"
+
+template <typename Readable, typename Buffer, typename Callback>
+static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
+ static_assert(std::is_trivial_v<Buffer>);
+ auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
+ r.async_read_some(
+ boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
+ if (!error.failed()) {
+ const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
+ std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
+ c(received_data);
+ }
+
+ AsyncReceiveInto(r, buffer, c);
+ });
+}
+
+template <typename Readable, typename Buffer>
+static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
+ static_assert(std::is_trivial_v<Buffer>);
+ auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
+ size_t bytes_read = r.read_some(boost_buffer);
+ const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
+ std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
+ return received_data;
+}
+
+enum class SignalType {
+ Stopped,
+ ShuttingDown,
+};
+
+struct SignalInfo {
+ SignalType type;
+ Kernel::KThread* thread;
+};
+
+namespace Core {
+
+class DebuggerImpl : public DebuggerBackend {
+public:
+ explicit DebuggerImpl(Core::System& system_, u16 port)
+ : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
+ frontend = std::make_unique<GDBStub>(*this, system);
+ InitializeServer(port);
+ }
+
+ ~DebuggerImpl() override {
+ ShutdownServer();
+ }
+
+ bool SignalDebugger(SignalInfo signal_info) {
+ std::scoped_lock lk{connection_lock};
+
+ if (stopped) {
+ // Do not notify the debugger about another event.
+ // It should be ignored.
+ return false;
+ }
+
+ // Set up the state.
+ stopped = true;
+ info = signal_info;
+
+ // Write a single byte into the pipe to wake up the debug interface.
+ boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
+ return true;
+ }
+
+ std::span<const u8> ReadFromClient() override {
+ return ReceiveInto(client_socket, client_data);
+ }
+
+ void WriteToClient(std::span<const u8> data) override {
+ boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
+ }
+
+ void SetActiveThread(Kernel::KThread* thread) override {
+ active_thread = thread;
+ }
+
+ Kernel::KThread* GetActiveThread() override {
+ return active_thread;
+ }
+
+private:
+ void InitializeServer(u16 port) {
+ using boost::asio::ip::tcp;
+
+ LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
+
+ // Run the connection thread.
+ connection_thread = std::jthread([&, port](std::stop_token stop_token) {
+ try {
+ // Initialize the listening socket and accept a new client.
+ tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
+ tcp::acceptor acceptor{io_context, endpoint};
+
+ acceptor.async_accept(client_socket, [](const auto&) {});
+ io_context.run_one();
+ io_context.restart();
+
+ if (stop_token.stop_requested()) {
+ return;
+ }
+
+ ThreadLoop(stop_token);
+ } catch (const std::exception& ex) {
+ LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
+ }
+ });
+ }
+
+ void ShutdownServer() {
+ connection_thread.request_stop();
+ io_context.stop();
+ connection_thread.join();
+ }
+
+ void ThreadLoop(std::stop_token stop_token) {
+ Common::SetCurrentThreadName("yuzu:Debugger");
+
+ // Set up the client signals for new data.
+ AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
+ AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
+
+ // Stop the emulated CPU.
+ AllCoreStop();
+
+ // Set the active thread.
+ UpdateActiveThread();
+
+ // Set up the frontend.
+ frontend->Connected();
+
+ // Main event loop.
+ while (!stop_token.stop_requested() && io_context.run()) {
+ }
+ }
+
+ void PipeData(std::span<const u8> data) {
+ switch (info.type) {
+ case SignalType::Stopped:
+ // Stop emulation.
+ AllCoreStop();
+
+ // Notify the client.
+ active_thread = info.thread;
+ UpdateActiveThread();
+ frontend->Stopped(active_thread);
+
+ break;
+ case SignalType::ShuttingDown:
+ frontend->ShuttingDown();
+
+ // Wait for emulation to shut down gracefully now.
+ suspend.reset();
+ signal_pipe.close();
+ client_socket.shutdown(boost::asio::socket_base::shutdown_both);
+ LOG_INFO(Debug_GDBStub, "Shut down server");
+
+ break;
+ }
+ }
+
+ void ClientData(std::span<const u8> data) {
+ const auto actions{frontend->ClientData(data)};
+ for (const auto action : actions) {
+ switch (action) {
+ case DebuggerAction::Interrupt: {
+ {
+ std::scoped_lock lk{connection_lock};
+ stopped = true;
+ }
+ AllCoreStop();
+ UpdateActiveThread();
+ frontend->Stopped(active_thread);
+ break;
+ }
+ case DebuggerAction::Continue:
+ active_thread->SetStepState(Kernel::StepState::NotStepping);
+ ResumeInactiveThreads();
+ AllCoreResume();
+ break;
+ case DebuggerAction::StepThreadUnlocked:
+ active_thread->SetStepState(Kernel::StepState::StepPending);
+ ResumeInactiveThreads();
+ AllCoreResume();
+ break;
+ case DebuggerAction::StepThreadLocked:
+ active_thread->SetStepState(Kernel::StepState::StepPending);
+ SuspendInactiveThreads();
+ AllCoreResume();
+ break;
+ case DebuggerAction::ShutdownEmulation: {
+ // Suspend all threads and release any locks held
+ active_thread->RequestSuspend(Kernel::SuspendType::Debug);
+ SuspendInactiveThreads();
+ AllCoreResume();
+
+ // Spawn another thread that will exit after shutdown,
+ // to avoid a deadlock
+ Core::System* system_ref{&system};
+ std::thread t([system_ref] { system_ref->Exit(); });
+ t.detach();
+ break;
+ }
+ }
+ }
+ }
+
+ void AllCoreStop() {
+ if (!suspend) {
+ suspend = system.StallCPU();
+ }
+ }
+
+ void AllCoreResume() {
+ stopped = false;
+ system.UnstallCPU();
+ suspend.reset();
+ }
+
+ void SuspendInactiveThreads() {
+ for (auto* thread : ThreadList()) {
+ if (thread != active_thread) {
+ thread->RequestSuspend(Kernel::SuspendType::Debug);
+ }
+ }
+ }
+
+ void ResumeInactiveThreads() {
+ for (auto* thread : ThreadList()) {
+ if (thread != active_thread) {
+ thread->Resume(Kernel::SuspendType::Debug);
+ thread->SetStepState(Kernel::StepState::NotStepping);
+ }
+ }
+ }
+
+ void UpdateActiveThread() {
+ const auto& threads{ThreadList()};
+ if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
+ active_thread = threads[0];
+ }
+ active_thread->Resume(Kernel::SuspendType::Debug);
+ active_thread->SetStepState(Kernel::StepState::NotStepping);
+ }
+
+ const std::vector<Kernel::KThread*>& ThreadList() {
+ return system.GlobalSchedulerContext().GetThreadList();
+ }
+
+private:
+ System& system;
+ std::unique_ptr<DebuggerFrontend> frontend;
+
+ std::jthread connection_thread;
+ std::mutex connection_lock;
+ boost::asio::io_context io_context;
+ boost::process::async_pipe signal_pipe;
+ boost::asio::ip::tcp::socket client_socket;
+ std::optional<std::unique_lock<std::mutex>> suspend;
+
+ SignalInfo info;
+ Kernel::KThread* active_thread;
+ bool pipe_data;
+ bool stopped;
+
+ std::array<u8, 4096> client_data;
+};
+
+Debugger::Debugger(Core::System& system, u16 port) {
+ try {
+ impl = std::make_unique<DebuggerImpl>(system, port);
+ } catch (const std::exception& ex) {
+ LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
+ }
+}
+
+Debugger::~Debugger() = default;
+
+bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
+ return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread});
+}
+
+void Debugger::NotifyShutdown() {
+ if (impl) {
+ impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr});
+ }
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+}
+
+namespace Core {
+class System;
+
+class DebuggerImpl;
+
+class Debugger {
+public:
+ /**
+ * Blocks and waits for a connection on localhost, port `server_port`.
+ * Does not create the debugger if the port is already in use.
+ */
+ explicit Debugger(Core::System& system, u16 server_port);
+ ~Debugger();
+
+ /**
+ * Notify the debugger that the given thread is stopped
+ * (due to a breakpoint, or due to stopping after a successful step).
+ *
+ * The debugger will asynchronously halt emulation after the notification has
+ * occurred. If another thread attempts to notify before emulation has stopped,
+ * it is ignored and this method will return false. Otherwise it will return true.
+ */
+ bool NotifyThreadStopped(Kernel::KThread* thread);
+
+ /**
+ * Notify the debugger that a shutdown is being performed now and disconnect.
+ */
+ void NotifyShutdown();
+
+private:
+ std::unique_ptr<DebuggerImpl> impl;
+};
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <functional>
+#include <span>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+}
+
+namespace Core {
+
+enum class DebuggerAction {
+ Interrupt, ///< Stop emulation as soon as possible.
+ Continue, ///< Resume emulation.
+ StepThreadLocked, ///< Step the currently-active thread without resuming others.
+ StepThreadUnlocked, ///< Step the currently-active thread and resume others.
+ ShutdownEmulation, ///< Shut down the emulator.
+};
+
+class DebuggerBackend {
+public:
+ virtual ~DebuggerBackend() = default;
+
+ /**
+ * Can be invoked from a callback to synchronously wait for more data.
+ * Will return as soon as least one byte is received. Reads up to 4096 bytes.
+ */
+ virtual std::span<const u8> ReadFromClient() = 0;
+
+ /**
+ * Can be invoked from a callback to write data to the client.
+ * Returns immediately after the data is sent.
+ */
+ virtual void WriteToClient(std::span<const u8> data) = 0;
+
+ /**
+ * Gets the currently active thread when the debugger is stopped.
+ */
+ virtual Kernel::KThread* GetActiveThread() = 0;
+
+ /**
+ * Sets the currently active thread when the debugger is stopped.
+ */
+ virtual void SetActiveThread(Kernel::KThread* thread) = 0;
+};
+
+class DebuggerFrontend {
+public:
+ explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
+
+ virtual ~DebuggerFrontend() = default;
+
+ /**
+ * Called after the client has successfully connected to the port.
+ */
+ virtual void Connected() = 0;
+
+ /**
+ * Called when emulation has stopped.
+ */
+ virtual void Stopped(Kernel::KThread* thread) = 0;
+
+ /**
+ * Called when emulation is shutting down.
+ */
+ virtual void ShuttingDown() = 0;
+
+ /**
+ * Called when new data is asynchronously received on the client socket.
+ * A list of actions to perform is returned.
+ */
+ [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
+
+protected:
+ DebuggerBackend& backend;
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <atomic>
+#include <numeric>
+#include <optional>
+#include <thread>
+
+#include <boost/algorithm/string.hpp>
+
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/debugger/gdbstub.h"
+#include "core/debugger/gdbstub_arch.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/loader/loader.h"
+#include "core/memory.h"
+
+namespace Core {
+
+constexpr char GDB_STUB_START = '$';
+constexpr char GDB_STUB_END = '#';
+constexpr char GDB_STUB_ACK = '+';
+constexpr char GDB_STUB_NACK = '-';
+constexpr char GDB_STUB_INT3 = 0x03;
+constexpr int GDB_STUB_SIGTRAP = 5;
+
+constexpr char GDB_STUB_REPLY_ERR[] = "E01";
+constexpr char GDB_STUB_REPLY_OK[] = "OK";
+constexpr char GDB_STUB_REPLY_EMPTY[] = "";
+
+static u8 CalculateChecksum(std::string_view data) {
+ return std::accumulate(data.begin(), data.end(), u8{0},
+ [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
+}
+
+static std::string EscapeGDB(std::string_view data) {
+ std::string escaped;
+ escaped.reserve(data.size());
+
+ for (char c : data) {
+ switch (c) {
+ case '#':
+ escaped += "}\x03";
+ break;
+ case '$':
+ escaped += "}\x04";
+ break;
+ case '*':
+ escaped += "}\x0a";
+ break;
+ case '}':
+ escaped += "}\x5d";
+ break;
+ default:
+ escaped += c;
+ break;
+ }
+ }
+
+ return escaped;
+}
+
+static std::string EscapeXML(std::string_view data) {
+ std::string escaped;
+ escaped.reserve(data.size());
+
+ for (char c : data) {
+ switch (c) {
+ case '&':
+ escaped += "&amp;";
+ break;
+ case '"':
+ escaped += "&quot;";
+ break;
+ case '<':
+ escaped += "&lt;";
+ break;
+ case '>':
+ escaped += "&gt;";
+ break;
+ default:
+ escaped += c;
+ break;
+ }
+ }
+
+ return escaped;
+}
+
+GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
+ : DebuggerFrontend(backend_), system{system_} {
+ if (system.CurrentProcess()->Is64BitProcess()) {
+ arch = std::make_unique<GDBStubA64>();
+ } else {
+ arch = std::make_unique<GDBStubA32>();
+ }
+}
+
+GDBStub::~GDBStub() = default;
+
+void GDBStub::Connected() {}
+
+void GDBStub::ShuttingDown() {}
+
+void GDBStub::Stopped(Kernel::KThread* thread) {
+ SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
+}
+
+std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
+ std::vector<DebuggerAction> actions;
+ current_command.insert(current_command.end(), data.begin(), data.end());
+
+ while (current_command.size() != 0) {
+ ProcessData(actions);
+ }
+
+ return actions;
+}
+
+void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
+ const char c{current_command[0]};
+
+ // Acknowledgement
+ if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
+ current_command.erase(current_command.begin());
+ return;
+ }
+
+ // Interrupt
+ if (c == GDB_STUB_INT3) {
+ LOG_INFO(Debug_GDBStub, "Received interrupt");
+ current_command.erase(current_command.begin());
+ actions.push_back(DebuggerAction::Interrupt);
+ SendStatus(GDB_STUB_ACK);
+ return;
+ }
+
+ // Otherwise, require the data to be the start of a command
+ if (c != GDB_STUB_START) {
+ LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
+ current_command.clear();
+ SendStatus(GDB_STUB_NACK);
+ return;
+ }
+
+ // Continue reading until command is complete
+ while (CommandEnd() == current_command.end()) {
+ const auto new_data{backend.ReadFromClient()};
+ current_command.insert(current_command.end(), new_data.begin(), new_data.end());
+ }
+
+ // Execute and respond to GDB
+ const auto command{DetachCommand()};
+
+ if (command) {
+ SendStatus(GDB_STUB_ACK);
+ ExecuteCommand(*command, actions);
+ } else {
+ SendStatus(GDB_STUB_NACK);
+ }
+}
+
+void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
+ LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
+
+ if (packet.length() == 0) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
+
+ if (packet.starts_with("vCont")) {
+ HandleVCont(packet.substr(5), actions);
+ return;
+ }
+
+ std::string_view command{packet.substr(1, packet.size())};
+
+ switch (packet[0]) {
+ case 'H': {
+ Kernel::KThread* thread{nullptr};
+ s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
+ if (thread_id >= 1) {
+ thread = GetThreadByID(thread_id);
+ } else {
+ thread = backend.GetActiveThread();
+ }
+
+ if (thread) {
+ SendReply(GDB_STUB_REPLY_OK);
+ backend.SetActiveThread(thread);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'T': {
+ s64 thread_id{strtoll(command.data(), nullptr, 16)};
+ if (GetThreadByID(thread_id)) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'Q':
+ case 'q':
+ HandleQuery(command);
+ break;
+ case '?':
+ SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
+ break;
+ case 'k':
+ LOG_INFO(Debug_GDBStub, "Shutting down emulation");
+ actions.push_back(DebuggerAction::ShutdownEmulation);
+ break;
+ case 'g':
+ SendReply(arch->ReadRegisters(backend.GetActiveThread()));
+ break;
+ case 'G':
+ arch->WriteRegisters(backend.GetActiveThread(), command);
+ SendReply(GDB_STUB_REPLY_OK);
+ break;
+ case 'p': {
+ const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ SendReply(arch->RegRead(backend.GetActiveThread(), reg));
+ break;
+ }
+ case 'P': {
+ const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
+ const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
+ break;
+ }
+ case 'm': {
+ const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
+
+ if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ std::vector<u8> mem(size);
+ system.Memory().ReadBlock(addr, mem.data(), size);
+
+ SendReply(Common::HexToString(mem));
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'M': {
+ const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
+
+ const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ const auto mem_substr{std::string_view(command).substr(mem_sep)};
+ const auto mem{Common::HexStringToVector(mem_substr, false)};
+
+ if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ system.Memory().WriteBlock(addr, mem.data(), size);
+ system.InvalidateCpuInstructionCacheRange(addr, size);
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 's':
+ actions.push_back(DebuggerAction::StepThreadLocked);
+ break;
+ case 'C':
+ case 'c':
+ actions.push_back(DebuggerAction::Continue);
+ break;
+ case 'Z': {
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+
+ if (system.Memory().IsValidVirtualAddress(addr)) {
+ replaced_instructions[addr] = system.Memory().Read32(addr);
+ system.Memory().Write32(addr, arch->BreakpointInstruction());
+ system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'z': {
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+
+ const auto orig_insn{replaced_instructions.find(addr)};
+ if (system.Memory().IsValidVirtualAddress(addr) &&
+ orig_insn != replaced_instructions.end()) {
+ system.Memory().Write32(addr, orig_insn->second);
+ system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+ replaced_instructions.erase(addr);
+
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ break;
+ }
+}
+
+// Structure offsets are from Atmosphere
+// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
+
+static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
+ const Kernel::KThread* thread) {
+ // Read thread type from TLS
+ const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
+ const VAddr argument_thread_type{thread->GetArgument()};
+
+ if (argument_thread_type && tls_thread_type != argument_thread_type) {
+ // Probably not created by nnsdk, no name available.
+ return std::nullopt;
+ }
+
+ if (!tls_thread_type) {
+ return std::nullopt;
+ }
+
+ const u16 version{memory.Read16(tls_thread_type + 0x26)};
+ VAddr name_pointer{};
+ if (version == 1) {
+ name_pointer = memory.Read32(tls_thread_type + 0xe4);
+ } else {
+ name_pointer = memory.Read32(tls_thread_type + 0xe8);
+ }
+
+ if (!name_pointer) {
+ // No name provided.
+ return std::nullopt;
+ }
+
+ return memory.ReadCString(name_pointer, 256);
+}
+
+static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
+ const Kernel::KThread* thread) {
+ // Read thread type from TLS
+ const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
+ const VAddr argument_thread_type{thread->GetArgument()};
+
+ if (argument_thread_type && tls_thread_type != argument_thread_type) {
+ // Probably not created by nnsdk, no name available.
+ return std::nullopt;
+ }
+
+ if (!tls_thread_type) {
+ return std::nullopt;
+ }
+
+ const u16 version{memory.Read16(tls_thread_type + 0x46)};
+ VAddr name_pointer{};
+ if (version == 1) {
+ name_pointer = memory.Read64(tls_thread_type + 0x1a0);
+ } else {
+ name_pointer = memory.Read64(tls_thread_type + 0x1a8);
+ }
+
+ if (!name_pointer) {
+ // No name provided.
+ return std::nullopt;
+ }
+
+ return memory.ReadCString(name_pointer, 256);
+}
+
+static std::optional<std::string> GetThreadName(Core::System& system,
+ const Kernel::KThread* thread) {
+ if (system.CurrentProcess()->Is64BitProcess()) {
+ return GetNameFromThreadType64(system.Memory(), thread);
+ } else {
+ return GetNameFromThreadType32(system.Memory(), thread);
+ }
+}
+
+static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
+ switch (thread->GetWaitReasonForDebugging()) {
+ case Kernel::ThreadWaitReasonForDebugging::Sleep:
+ return "Sleep";
+ case Kernel::ThreadWaitReasonForDebugging::IPC:
+ return "IPC";
+ case Kernel::ThreadWaitReasonForDebugging::Synchronization:
+ return "Synchronization";
+ case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
+ return "ConditionVar";
+ case Kernel::ThreadWaitReasonForDebugging::Arbitration:
+ return "Arbitration";
+ case Kernel::ThreadWaitReasonForDebugging::Suspended:
+ return "Suspended";
+ default:
+ return "Unknown";
+ }
+}
+
+static std::string GetThreadState(const Kernel::KThread* thread) {
+ switch (thread->GetState()) {
+ case Kernel::ThreadState::Initialized:
+ return "Initialized";
+ case Kernel::ThreadState::Waiting:
+ return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
+ case Kernel::ThreadState::Runnable:
+ return "Runnable";
+ case Kernel::ThreadState::Terminated:
+ return "Terminated";
+ default:
+ return "Unknown";
+ }
+}
+
+static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
+ const auto amount{request.substr(request.find(',') + 1)};
+ const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
+ const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
+
+ if (offset_val + amount_val > buffer.size()) {
+ return fmt::format("l{}", buffer.substr(offset_val));
+ } else {
+ return fmt::format("m{}", buffer.substr(offset_val, amount_val));
+ }
+}
+
+void GDBStub::HandleQuery(std::string_view command) {
+ if (command.starts_with("TStatus")) {
+ // no tracepoint support
+ SendReply("T0");
+ } else if (command.starts_with("Supported")) {
+ SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
+ "vContSupported+;QStartNoAckMode+");
+ } else if (command.starts_with("Xfer:features:read:target.xml:")) {
+ const auto target_xml{arch->GetTargetXML()};
+ SendReply(PaginateBuffer(target_xml, command.substr(30)));
+ } else if (command.starts_with("Offsets")) {
+ Loader::AppLoader::Modules modules;
+ system.GetAppLoader().ReadNSOModules(modules);
+
+ const auto main = std::find_if(modules.begin(), modules.end(),
+ [](const auto& key) { return key.second == "main"; });
+ if (main != modules.end()) {
+ SendReply(fmt::format("TextSeg={:x}", main->first));
+ } else {
+ SendReply(fmt::format("TextSeg={:x}",
+ system.CurrentProcess()->PageTable().GetCodeRegionStart()));
+ }
+ } else if (command.starts_with("Xfer:libraries:read::")) {
+ Loader::AppLoader::Modules modules;
+ system.GetAppLoader().ReadNSOModules(modules);
+
+ std::string buffer;
+ buffer += R"(<?xml version="1.0"?>)";
+ buffer += "<library-list>";
+ for (const auto& [base, name] : modules) {
+ buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
+ EscapeXML(name), base);
+ }
+ buffer += "</library-list>";
+
+ SendReply(PaginateBuffer(buffer, command.substr(21)));
+ } else if (command.starts_with("fThreadInfo")) {
+ // beginning of list
+ const auto& threads = system.GlobalSchedulerContext().GetThreadList();
+ std::vector<std::string> thread_ids;
+ for (const auto& thread : threads) {
+ thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
+ }
+ SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
+ } else if (command.starts_with("sThreadInfo")) {
+ // end of list
+ SendReply("l");
+ } else if (command.starts_with("Xfer:threads:read::")) {
+ std::string buffer;
+ buffer += R"(<?xml version="1.0"?>)";
+ buffer += "<threads>";
+
+ const auto& threads = system.GlobalSchedulerContext().GetThreadList();
+ for (const auto* thread : threads) {
+ auto thread_name{GetThreadName(system, thread)};
+ if (!thread_name) {
+ thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
+ }
+
+ buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
+ thread->GetThreadID(), thread->GetActiveCore(),
+ EscapeXML(*thread_name), GetThreadState(thread));
+ }
+
+ buffer += "</threads>";
+
+ SendReply(PaginateBuffer(buffer, command.substr(19)));
+ } else if (command.starts_with("Attached")) {
+ SendReply("0");
+ } else if (command.starts_with("StartNoAckMode")) {
+ no_ack = true;
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ }
+}
+
+void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
+ if (command == "?") {
+ // Continuing and stepping are supported
+ // (signal is ignored, but required for GDB to use vCont)
+ SendReply("vCont;c;C;s;S");
+ return;
+ }
+
+ Kernel::KThread* stepped_thread{nullptr};
+ bool lock_execution{true};
+
+ std::vector<std::string> entries;
+ boost::split(entries, command.substr(1), boost::is_any_of(";"));
+ for (const auto& thread_action : entries) {
+ std::vector<std::string> parts;
+ boost::split(parts, thread_action, boost::is_any_of(":"));
+
+ if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
+ lock_execution = false;
+ }
+ if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
+ stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
+ }
+ }
+
+ if (stepped_thread) {
+ backend.SetActiveThread(stepped_thread);
+ actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
+ : DebuggerAction::StepThreadUnlocked);
+ } else {
+ actions.push_back(DebuggerAction::Continue);
+ }
+}
+
+Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
+ const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
+ for (auto* thread : threads) {
+ if (thread->GetThreadID() == thread_id) {
+ return thread;
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<char>::const_iterator GDBStub::CommandEnd() const {
+ // Find the end marker
+ const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
+
+ // Require the checksum to be present
+ return std::min(end + 2, current_command.end());
+}
+
+std::optional<std::string> GDBStub::DetachCommand() {
+ // Slice the string part from the beginning to the end marker
+ const auto end{CommandEnd()};
+
+ // Extract possible command data
+ std::string data(current_command.data(), end - current_command.begin() + 1);
+
+ // Shift over the remaining contents
+ current_command.erase(current_command.begin(), end + 1);
+
+ // Validate received command
+ if (data[0] != GDB_STUB_START) {
+ LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
+ return std::nullopt;
+ }
+
+ u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
+ u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
+
+ // Verify checksum
+ if (calculated != received) {
+ LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
+ calculated, received);
+ return std::nullopt;
+ }
+
+ return data.substr(1, data.size() - 4);
+}
+
+void GDBStub::SendReply(std::string_view data) {
+ const auto escaped{EscapeGDB(data)};
+ const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
+ CalculateChecksum(escaped))};
+ LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
+
+ // C++ string support is complete rubbish
+ const u8* output_begin = reinterpret_cast<const u8*>(output.data());
+ const u8* output_end = output_begin + output.size();
+ backend.WriteToClient(std::span<const u8>(output_begin, output_end));
+}
+
+void GDBStub::SendStatus(char status) {
+ if (no_ack) {
+ return;
+ }
+
+ std::array<u8, 1> buf = {static_cast<u8>(status)};
+ LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
+ backend.WriteToClient(buf);
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <string_view>
+#include <vector>
+
+#include "core/debugger/debugger_interface.h"
+#include "core/debugger/gdbstub_arch.h"
+
+namespace Core {
+
+class System;
+
+class GDBStub : public DebuggerFrontend {
+public:
+ explicit GDBStub(DebuggerBackend& backend, Core::System& system);
+ ~GDBStub() override;
+
+ void Connected() override;
+ void Stopped(Kernel::KThread* thread) override;
+ void ShuttingDown() override;
+ std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
+
+private:
+ void ProcessData(std::vector<DebuggerAction>& actions);
+ void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
+ void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
+ void HandleQuery(std::string_view command);
+ std::vector<char>::const_iterator CommandEnd() const;
+ std::optional<std::string> DetachCommand();
+ Kernel::KThread* GetThreadByID(u64 thread_id);
+
+ void SendReply(std::string_view data);
+ void SendStatus(char status);
+
+private:
+ Core::System& system;
+ std::unique_ptr<GDBStubArch> arch;
+ std::vector<char> current_command;
+ std::map<VAddr, u32> replaced_instructions;
+ bool no_ack{};
+};
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/hex_util.h"
+#include "core/debugger/gdbstub_arch.h"
+#include "core/hle/kernel/k_thread.h"
+
+namespace Core {
+
+template <typename T>
+static T HexToValue(std::string_view hex) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T value{};
+ const auto mem{Common::HexStringToVector(hex, false)};
+ std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
+ return value;
+}
+
+template <typename T>
+static std::string ValueToHex(const T value) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::array<u8, sizeof(T)> mem{};
+ std::memcpy(mem.data(), &value, sizeof(T));
+ return Common::HexToString(mem);
+}
+
+template <typename T>
+static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T value{};
+ std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
+ sizeof(T));
+ return value;
+}
+
+template <typename T>
+static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
+}
+
+// For sample XML files see the GDB source /gdb/features
+// This XML defines what the registers are for this specific ARM device
+std::string GDBStubA64::GetTargetXML() const {
+ constexpr const char* target_xml =
+ R"(<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target version="1.0">
+ <architecture>aarch64</architecture>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <reg name="x0" bitsize="64"/>
+ <reg name="x1" bitsize="64"/>
+ <reg name="x2" bitsize="64"/>
+ <reg name="x3" bitsize="64"/>
+ <reg name="x4" bitsize="64"/>
+ <reg name="x5" bitsize="64"/>
+ <reg name="x6" bitsize="64"/>
+ <reg name="x7" bitsize="64"/>
+ <reg name="x8" bitsize="64"/>
+ <reg name="x9" bitsize="64"/>
+ <reg name="x10" bitsize="64"/>
+ <reg name="x11" bitsize="64"/>
+ <reg name="x12" bitsize="64"/>
+ <reg name="x13" bitsize="64"/>
+ <reg name="x14" bitsize="64"/>
+ <reg name="x15" bitsize="64"/>
+ <reg name="x16" bitsize="64"/>
+ <reg name="x17" bitsize="64"/>
+ <reg name="x18" bitsize="64"/>
+ <reg name="x19" bitsize="64"/>
+ <reg name="x20" bitsize="64"/>
+ <reg name="x21" bitsize="64"/>
+ <reg name="x22" bitsize="64"/>
+ <reg name="x23" bitsize="64"/>
+ <reg name="x24" bitsize="64"/>
+ <reg name="x25" bitsize="64"/>
+ <reg name="x26" bitsize="64"/>
+ <reg name="x27" bitsize="64"/>
+ <reg name="x28" bitsize="64"/>
+ <reg name="x29" bitsize="64"/>
+ <reg name="x30" bitsize="64"/>
+ <reg name="sp" bitsize="64" type="data_ptr"/>
+ <reg name="pc" bitsize="64" type="code_ptr"/>
+ <flags id="cpsr_flags" size="4">
+ <field name="SP" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="EL" start="2" end="3"/>
+ <field name="nRW" start="4" end="4"/>
+ <field name="" start="5" end="5"/>
+ <field name="F" start="6" end="6"/>
+ <field name="I" start="7" end="7"/>
+ <field name="A" start="8" end="8"/>
+ <field name="D" start="9" end="9"/>
+ <field name="IL" start="20" end="20"/>
+ <field name="SS" start="21" end="21"/>
+ <field name="V" start="28" end="28"/>
+ <field name="C" start="29" end="29"/>
+ <field name="Z" start="30" end="30"/>
+ <field name="N" start="31" end="31"/>
+ </flags>
+ <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
+ </feature>
+ <feature name="org.gnu.gdb.aarch64.fpu">
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v2u" type="uint64" count="2"/>
+ <vector id="v2i" type="int64" count="2"/>
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4u" type="uint32" count="4"/>
+ <vector id="v4i" type="int32" count="4"/>
+ <vector id="v8u" type="uint16" count="8"/>
+ <vector id="v8i" type="int16" count="8"/>
+ <vector id="v16u" type="uint8" count="16"/>
+ <vector id="v16i" type="int8" count="16"/>
+ <vector id="v1u" type="uint128" count="1"/>
+ <vector id="v1i" type="int128" count="1"/>
+ <union id="vnd">
+ <field name="f" type="v2d"/>
+ <field name="u" type="v2u"/>
+ <field name="s" type="v2i"/>
+ </union>
+ <union id="vns">
+ <field name="f" type="v4f"/>
+ <field name="u" type="v4u"/>
+ <field name="s" type="v4i"/>
+ </union>
+ <union id="vnh">
+ <field name="u" type="v8u"/>
+ <field name="s" type="v8i"/>
+ </union>
+ <union id="vnb">
+ <field name="u" type="v16u"/>
+ <field name="s" type="v16i"/>
+ </union>
+ <union id="vnq">
+ <field name="u" type="v1u"/>
+ <field name="s" type="v1i"/>
+ </union>
+ <union id="aarch64v">
+ <field name="d" type="vnd"/>
+ <field name="s" type="vns"/>
+ <field name="h" type="vnh"/>
+ <field name="b" type="vnb"/>
+ <field name="q" type="vnq"/>
+ </union>
+ <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
+ <reg name="v1" bitsize="128" type="aarch64v" />
+ <reg name="v2" bitsize="128" type="aarch64v" />
+ <reg name="v3" bitsize="128" type="aarch64v" />
+ <reg name="v4" bitsize="128" type="aarch64v" />
+ <reg name="v5" bitsize="128" type="aarch64v" />
+ <reg name="v6" bitsize="128" type="aarch64v" />
+ <reg name="v7" bitsize="128" type="aarch64v" />
+ <reg name="v8" bitsize="128" type="aarch64v" />
+ <reg name="v9" bitsize="128" type="aarch64v" />
+ <reg name="v10" bitsize="128" type="aarch64v"/>
+ <reg name="v11" bitsize="128" type="aarch64v"/>
+ <reg name="v12" bitsize="128" type="aarch64v"/>
+ <reg name="v13" bitsize="128" type="aarch64v"/>
+ <reg name="v14" bitsize="128" type="aarch64v"/>
+ <reg name="v15" bitsize="128" type="aarch64v"/>
+ <reg name="v16" bitsize="128" type="aarch64v"/>
+ <reg name="v17" bitsize="128" type="aarch64v"/>
+ <reg name="v18" bitsize="128" type="aarch64v"/>
+ <reg name="v19" bitsize="128" type="aarch64v"/>
+ <reg name="v20" bitsize="128" type="aarch64v"/>
+ <reg name="v21" bitsize="128" type="aarch64v"/>
+ <reg name="v22" bitsize="128" type="aarch64v"/>
+ <reg name="v23" bitsize="128" type="aarch64v"/>
+ <reg name="v24" bitsize="128" type="aarch64v"/>
+ <reg name="v25" bitsize="128" type="aarch64v"/>
+ <reg name="v26" bitsize="128" type="aarch64v"/>
+ <reg name="v27" bitsize="128" type="aarch64v"/>
+ <reg name="v28" bitsize="128" type="aarch64v"/>
+ <reg name="v29" bitsize="128" type="aarch64v"/>
+ <reg name="v30" bitsize="128" type="aarch64v"/>
+ <reg name="v31" bitsize="128" type="aarch64v"/>
+ <reg name="fpsr" bitsize="32"/>
+ <reg name="fpcr" bitsize="32"/>
+ </feature>
+</target>)";
+
+ return target_xml;
+}
+
+std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
+ if (!thread) {
+ return "";
+ }
+
+ const auto& context{thread->GetContext64()};
+ const auto& gprs{context.cpu_registers};
+ const auto& fprs{context.vector_registers};
+
+ if (id <= SP_REGISTER) {
+ return ValueToHex(gprs[id]);
+ } else if (id == PC_REGISTER) {
+ return ValueToHex(context.pc);
+ } else if (id == PSTATE_REGISTER) {
+ return ValueToHex(context.pstate);
+ } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
+ return ValueToHex(fprs[id - Q0_REGISTER]);
+ } else if (id == FPSR_REGISTER) {
+ return ValueToHex(context.fpsr);
+ } else if (id == FPCR_REGISTER) {
+ return ValueToHex(context.fpcr);
+ } else {
+ return "";
+ }
+}
+
+void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
+ if (!thread) {
+ return;
+ }
+
+ auto& context{thread->GetContext64()};
+
+ if (id <= SP_REGISTER) {
+ context.cpu_registers[id] = HexToValue<u64>(value);
+ } else if (id == PC_REGISTER) {
+ context.pc = HexToValue<u64>(value);
+ } else if (id == PSTATE_REGISTER) {
+ context.pstate = HexToValue<u32>(value);
+ } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
+ context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
+ } else if (id == FPSR_REGISTER) {
+ context.fpsr = HexToValue<u32>(value);
+ } else if (id == FPCR_REGISTER) {
+ context.fpcr = HexToValue<u32>(value);
+ }
+}
+
+std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
+ std::string output;
+
+ for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
+ output += RegRead(thread, reg);
+ }
+
+ return output;
+}
+
+void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
+ for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
+ if (reg <= SP_REGISTER || reg == PC_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 16));
+ i += 16;
+ } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 8));
+ i += 8;
+ } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 32));
+ i += 32;
+ }
+ }
+}
+
+std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
+ return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
+ RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
+ LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
+}
+
+u32 GDBStubA64::BreakpointInstruction() const {
+ // A64: brk #0
+ return 0xd4200000;
+}
+
+std::string GDBStubA32::GetTargetXML() const {
+ constexpr const char* target_xml =
+ R"(<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target version="1.0">
+ <architecture>arm</architecture>
+ <feature name="org.gnu.gdb.arm.core">
+ <reg name="r0" bitsize="32" type="uint32"/>
+ <reg name="r1" bitsize="32" type="uint32"/>
+ <reg name="r2" bitsize="32" type="uint32"/>
+ <reg name="r3" bitsize="32" type="uint32"/>
+ <reg name="r4" bitsize="32" type="uint32"/>
+ <reg name="r5" bitsize="32" type="uint32"/>
+ <reg name="r6" bitsize="32" type="uint32"/>
+ <reg name="r7" bitsize="32" type="uint32"/>
+ <reg name="r8" bitsize="32" type="uint32"/>
+ <reg name="r9" bitsize="32" type="uint32"/>
+ <reg name="r10" bitsize="32" type="uint32"/>
+ <reg name="r11" bitsize="32" type="uint32"/>
+ <reg name="r12" bitsize="32" type="uint32"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="lr" bitsize="32" type="code_ptr"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsr" bitsize="32" regnum="25"/>
+ </feature>
+ <feature name="org.gnu.gdb.arm.vfp">
+ <vector id="neon_uint8x8" type="uint8" count="8"/>
+ <vector id="neon_uint16x4" type="uint16" count="4"/>
+ <vector id="neon_uint32x2" type="uint32" count="2"/>
+ <vector id="neon_float32x2" type="ieee_single" count="2"/>
+ <union id="neon_d">
+ <field name="u8" type="neon_uint8x8"/>
+ <field name="u16" type="neon_uint16x4"/>
+ <field name="u32" type="neon_uint32x2"/>
+ <field name="u64" type="uint64"/>
+ <field name="f32" type="neon_float32x2"/>
+ <field name="f64" type="ieee_double"/>
+ </union>
+ <vector id="neon_uint8x16" type="uint8" count="16"/>
+ <vector id="neon_uint16x8" type="uint16" count="8"/>
+ <vector id="neon_uint32x4" type="uint32" count="4"/>
+ <vector id="neon_uint64x2" type="uint64" count="2"/>
+ <vector id="neon_float32x4" type="ieee_single" count="4"/>
+ <vector id="neon_float64x2" type="ieee_double" count="2"/>
+ <union id="neon_q">
+ <field name="u8" type="neon_uint8x16"/>
+ <field name="u16" type="neon_uint16x8"/>
+ <field name="u32" type="neon_uint32x4"/>
+ <field name="u64" type="neon_uint64x2"/>
+ <field name="f32" type="neon_float32x4"/>
+ <field name="f64" type="neon_float64x2"/>
+ </union>
+ <reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
+ <reg name="d1" bitsize="64" type="neon_d"/>
+ <reg name="d2" bitsize="64" type="neon_d"/>
+ <reg name="d3" bitsize="64" type="neon_d"/>
+ <reg name="d4" bitsize="64" type="neon_d"/>
+ <reg name="d5" bitsize="64" type="neon_d"/>
+ <reg name="d6" bitsize="64" type="neon_d"/>
+ <reg name="d7" bitsize="64" type="neon_d"/>
+ <reg name="d8" bitsize="64" type="neon_d"/>
+ <reg name="d9" bitsize="64" type="neon_d"/>
+ <reg name="d10" bitsize="64" type="neon_d"/>
+ <reg name="d11" bitsize="64" type="neon_d"/>
+ <reg name="d12" bitsize="64" type="neon_d"/>
+ <reg name="d13" bitsize="64" type="neon_d"/>
+ <reg name="d14" bitsize="64" type="neon_d"/>
+ <reg name="d15" bitsize="64" type="neon_d"/>
+ <reg name="d16" bitsize="64" type="neon_d"/>
+ <reg name="d17" bitsize="64" type="neon_d"/>
+ <reg name="d18" bitsize="64" type="neon_d"/>
+ <reg name="d19" bitsize="64" type="neon_d"/>
+ <reg name="d20" bitsize="64" type="neon_d"/>
+ <reg name="d21" bitsize="64" type="neon_d"/>
+ <reg name="d22" bitsize="64" type="neon_d"/>
+ <reg name="d23" bitsize="64" type="neon_d"/>
+ <reg name="d24" bitsize="64" type="neon_d"/>
+ <reg name="d25" bitsize="64" type="neon_d"/>
+ <reg name="d26" bitsize="64" type="neon_d"/>
+ <reg name="d27" bitsize="64" type="neon_d"/>
+ <reg name="d28" bitsize="64" type="neon_d"/>
+ <reg name="d29" bitsize="64" type="neon_d"/>
+ <reg name="d30" bitsize="64" type="neon_d"/>
+ <reg name="d31" bitsize="64" type="neon_d"/>
+
+ <reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
+ <reg name="q1" bitsize="128" type="neon_q"/>
+ <reg name="q2" bitsize="128" type="neon_q"/>
+ <reg name="q3" bitsize="128" type="neon_q"/>
+ <reg name="q4" bitsize="128" type="neon_q"/>
+ <reg name="q5" bitsize="128" type="neon_q"/>
+ <reg name="q6" bitsize="128" type="neon_q"/>
+ <reg name="q7" bitsize="128" type="neon_q"/>
+ <reg name="q8" bitsize="128" type="neon_q"/>
+ <reg name="q9" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q12" bitsize="128" type="neon_q"/>
+ <reg name="q13" bitsize="128" type="neon_q"/>
+ <reg name="q14" bitsize="128" type="neon_q"/>
+ <reg name="q15" bitsize="128" type="neon_q"/>
+
+ <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
+ </feature>
+</target>)";
+
+ return target_xml;
+}
+
+std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
+ if (!thread) {
+ return "";
+ }
+
+ const auto& context{thread->GetContext32()};
+ const auto& gprs{context.cpu_registers};
+ const auto& fprs{context.extension_registers};
+
+ if (id <= PC_REGISTER) {
+ return ValueToHex(gprs[id]);
+ } else if (id == CPSR_REGISTER) {
+ return ValueToHex(context.cpsr);
+ } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
+ const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
+ return ValueToHex(dN);
+ } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
+ const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
+ return ValueToHex(qN);
+ } else if (id == FPSCR_REGISTER) {
+ return ValueToHex(context.fpscr);
+ } else {
+ return "";
+ }
+}
+
+void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
+ if (!thread) {
+ return;
+ }
+
+ auto& context{thread->GetContext32()};
+ auto& fprs{context.extension_registers};
+
+ if (id <= PC_REGISTER) {
+ context.cpu_registers[id] = HexToValue<u32>(value);
+ } else if (id == CPSR_REGISTER) {
+ context.cpsr = HexToValue<u32>(value);
+ } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
+ PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
+ } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
+ PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
+ } else if (id == FPSCR_REGISTER) {
+ context.fpscr = HexToValue<u32>(value);
+ }
+}
+
+std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
+ std::string output;
+
+ for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
+ const bool gpr{reg <= PC_REGISTER};
+ const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
+ const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
+
+ if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
+ continue;
+ }
+
+ output += RegRead(thread, reg);
+ }
+
+ return output;
+}
+
+void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
+ for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
+ const bool gpr{reg <= PC_REGISTER};
+ const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
+ const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
+
+ if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 8));
+ i += 8;
+ } else if (dfpr) {
+ RegWrite(thread, reg, register_data.substr(i, 16));
+ i += 16;
+ } else if (qfpr) {
+ RegWrite(thread, reg, register_data.substr(i, 32));
+ i += 32;
+ }
+
+ if (reg == PC_REGISTER) {
+ reg = CPSR_REGISTER - 1;
+ } else if (reg == CPSR_REGISTER) {
+ reg = D0_REGISTER - 1;
+ }
+ }
+}
+
+std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
+ return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
+ RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
+ LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
+}
+
+u32 GDBStubA32::BreakpointInstruction() const {
+ // A32: trap
+ // T32: trap + b #4
+ return 0xe7ffdefe;
+}
+
+} // 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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+}
+
+namespace Core {
+
+class GDBStubArch {
+public:
+ virtual ~GDBStubArch() = default;
+ virtual std::string GetTargetXML() const = 0;
+ virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
+ virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
+ virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
+ virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
+ virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
+ virtual u32 BreakpointInstruction() const = 0;
+};
+
+class GDBStubA64 final : public GDBStubArch {
+public:
+ std::string GetTargetXML() const override;
+ std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
+ void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
+ std::string ReadRegisters(const Kernel::KThread* thread) const override;
+ void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
+ std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
+ u32 BreakpointInstruction() const override;
+
+private:
+ static constexpr u32 LR_REGISTER = 30;
+ static constexpr u32 SP_REGISTER = 31;
+ static constexpr u32 PC_REGISTER = 32;
+ static constexpr u32 PSTATE_REGISTER = 33;
+ static constexpr u32 Q0_REGISTER = 34;
+ static constexpr u32 FPSR_REGISTER = 66;
+ static constexpr u32 FPCR_REGISTER = 67;
+};
+
+class GDBStubA32 final : public GDBStubArch {
+public:
+ std::string GetTargetXML() const override;
+ std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
+ void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
+ std::string ReadRegisters(const Kernel::KThread* thread) const override;
+ void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
+ std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
+ u32 BreakpointInstruction() const override;
+
+private:
+ static constexpr u32 SP_REGISTER = 13;
+ static constexpr u32 LR_REGISTER = 14;
+ static constexpr u32 PC_REGISTER = 15;
+ static constexpr u32 CPSR_REGISTER = 25;
+ static constexpr u32 D0_REGISTER = 32;
+ static constexpr u32 Q0_REGISTER = 64;
+ static constexpr u32 FPSCR_REGISTER = 80;
+};
+
+} // 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
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
- Core::Crypto::Key128 out;
+ Core::Crypto::Key128 out{};
if (type == NCASectionCryptoType::XTS) {
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
} 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
low = mid + 1;
}
}
- UNREACHABLE_MSG("Offset could not be found in BKTR block.");
+ ASSERT_MSG(false, "Offset could not be found in BKTR block.");
return {0, 0};
}
} // 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) {
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
return ContentRecordType::HtmlDocument;
default:
- UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type);
+ ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type);
return ContentRecordType{};
}
}
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_
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
} else {
- UNREACHABLE();
+ ASSERT(false);
return nullptr;
}
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
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
} else {
- UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
+ ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
}
}
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() {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
+
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
const auto button_index =
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
}
bool EmulatedController::TestVibration(std::size_t device_index) {
- static constexpr VibrationValue test_vibration = {
+ if (device_index >= output_devices.size()) {
+ return false;
+ }
+ if (!output_devices[device_index]) {
+ return false;
+ }
+
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+
+ if (!player.vibration_enabled) {
+ return false;
+ }
+
+ const Common::Input::VibrationStatus test_vibration = {
.low_amplitude = 0.001f,
- .low_frequency = 160.0f,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = 0.001f,
- .high_frequency = 320.0f,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
+ };
+
+ const Common::Input::VibrationStatus zero_vibration = {
+ .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
+ .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
};
// Send a slight vibration to test for rumble support
- SetVibration(device_index, test_vibration);
+ output_devices[device_index]->SetVibration(test_vibration);
// Stop any vibration and return the result
- return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
+ return output_devices[device_index]->SetVibration(zero_vibration) ==
+ Common::Input::VibrationError::None;
}
bool 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) {
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
@@ -77,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
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 {
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
+// This is nn::hid::server::SixAxisSensorProperties
+struct SixAxisSensorProperties {
+ union {
+ u8 raw{};
+ BitField<0, 1, u8> is_newly_assigned;
+ BitField<1, 1, u8> is_firmware_update_available;
+ };
+};
+static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
+
+// This is nn::hid::SixAxisSensorCalibrationParameter
+struct SixAxisSensorCalibrationParameter {
+ std::array<u8, 0x744> unknown_data{};
+};
+static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
+ "SixAxisSensorCalibrationParameter is an invalid size");
+
+// This is nn::hid::SixAxisSensorIcInformation
+struct SixAxisSensorIcInformation {
+ f32 angular_rate{2000.0f}; // dps
+ std::array<f32, 6> unknown_gyro_data1{
+ -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
+ }; // dps
+ std::array<f32, 9> unknown_gyro_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_gyro_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+ f32 acceleration_range{8.0f}; // g force
+ std::array<f32, 6> unknown_accel_data1{
+ -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
+ }; // g force
+ std::array<f32, 9> unknown_accel_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_accel_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+};
+static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
+ "SixAxisSensorIcInformation is an invalid size");
+
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
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 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
#include <random>
#include "common/input.h"
@@ -196,6 +197,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
+ // Limit id to maximum number of fingers
+ status.id = std::clamp(status.id, 0, 16);
+
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}
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:
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
- UNREACHABLE_MSG("Unexpected handler index {}", index);
+ ASSERT_MSG(false, "Unexpected handler index {}", index);
}
}
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) {
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
- UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
+ ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
}
// 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:
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
- UNREACHABLE();
+ ASSERT(false);
return ResultUnknown;
}
@@ -49,7 +49,7 @@ public:
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
- UNREACHABLE();
+ ASSERT(false);
return ResultUnknown;
}
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) {
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
- UNREACHABLE();
+ ASSERT(false);
return 0;
}
@@ -101,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
- UNREACHABLE();
+ ASSERT(false);
return 0;
}
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 {
class KernelCore;
class KProcess;
-#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
+#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
@@ -40,16 +40,19 @@ public:
static constexpr const char* GetStaticTypeName() { \
return TypeName; \
} \
- virtual TypeObj GetTypeObj() const { \
+ virtual TypeObj GetTypeObj() ATTRIBUTE { \
return GetStaticTypeObj(); \
} \
- virtual const char* GetTypeName() const { \
+ virtual const char* GetTypeName() ATTRIBUTE { \
return GetStaticTypeName(); \
} \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
+#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
+ KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
+
class KAutoObject {
protected:
class TypeObj {
@@ -82,7 +85,7 @@ protected:
};
private:
- KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
+ KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
public:
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:
}
}
}
+ UNREACHABLE();
}();
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
auto& page_table = m_owner->PageTable();
// Construct the page group.
- m_page_group =
- KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
+ m_page_group = {};
// Lock the memory.
- R_TRY(page_table.LockForCodeMemory(addr, size))
+ R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
// Clear the memory.
- //
- // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
- // guest memory, and is not specifically required if the guest program is correctly
- // written, so disable until this is further investigated.
- //
- // for (const auto& block : m_page_group.Nodes()) {
- // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
- // }
+ for (const auto& block : m_page_group.Nodes()) {
+ std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
+ }
// Set remaining tracking members.
+ m_owner->Open();
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
@@ -57,8 +52,14 @@ void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
- m_owner->PageTable().UnlockForCodeMemory(m_address, size);
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
}
+
+ // Close the page group.
+ m_page_group = {};
+
+ // Close our reference to our owner.
+ m_owner->Close();
}
ResultCode KCodeMemory::Map(VAddr address, size_t size) {
@@ -118,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
- break;
+ // Already validated by ControlCodeMemory svc
+ UNREACHABLE();
}
// 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) {
} else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
return KMemoryManager::Pool::SystemNonSecure;
} else {
- UNREACHABLE_MSG("InvalidMemoryRegionType for conversion to Pool");
+ ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
return {};
}
}
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
case FileSys::ProgramAddressSpaceType::Is39Bit:
return 39;
default:
- UNREACHABLE();
+ ASSERT(false);
return {};
}
}
@@ -128,7 +128,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
const std::size_t needed_size{
(alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
if (alloc_size < needed_size) {
- UNREACHABLE();
+ ASSERT(false);
return ResultOutOfMemory;
}
@@ -542,6 +542,95 @@ ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num
return ResultSuccess;
}
+bool KPageTable::IsValidPageGroup(const KPageLinkedList& pg_ll, VAddr addr, size_t num_pages) {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ const size_t size = num_pages * PageSize;
+ const auto& pg = pg_ll.Nodes();
+ const auto& memory_layout = system.Kernel().MemoryLayout();
+
+ // Empty groups are necessarily invalid.
+ if (pg.empty()) {
+ return false;
+ }
+
+ // We're going to validate that the group we'd expect is the group we see.
+ auto cur_it = pg.begin();
+ PAddr cur_block_address = cur_it->GetAddress();
+ size_t cur_block_pages = cur_it->GetNumPages();
+
+ auto UpdateCurrentIterator = [&]() {
+ if (cur_block_pages == 0) {
+ if ((++cur_it) == pg.end()) {
+ return false;
+ }
+
+ cur_block_address = cur_it->GetAddress();
+ cur_block_pages = cur_it->GetNumPages();
+ }
+ return true;
+ };
+
+ // Begin traversal.
+ Common::PageTable::TraversalContext context;
+ Common::PageTable::TraversalEntry next_entry;
+ if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
+ return false;
+ }
+
+ // Prepare tracking variables.
+ PAddr cur_addr = next_entry.phys_addr;
+ size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
+ size_t tot_size = cur_size;
+
+ // Iterate, comparing expected to actual.
+ while (tot_size < size) {
+ if (!page_table_impl.ContinueTraversal(next_entry, context)) {
+ return false;
+ }
+
+ if (next_entry.phys_addr != (cur_addr + cur_size)) {
+ const size_t cur_pages = cur_size / PageSize;
+
+ if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
+ return false;
+ }
+
+ if (!UpdateCurrentIterator()) {
+ return false;
+ }
+
+ if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
+ return false;
+ }
+
+ cur_block_address += cur_size;
+ cur_block_pages -= cur_pages;
+ cur_addr = next_entry.phys_addr;
+ cur_size = next_entry.block_size;
+ } else {
+ cur_size += next_entry.block_size;
+ }
+
+ tot_size += next_entry.block_size;
+ }
+
+ // Ensure we compare the right amount for the last block.
+ if (tot_size > size) {
+ cur_size -= (tot_size - size);
+ }
+
+ if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
+ return false;
+ }
+
+ if (!UpdateCurrentIterator()) {
+ return false;
+ }
+
+ return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
+}
+
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
KPageTable& src_page_table, VAddr src_addr) {
KScopedLightLock lk(general_lock);
@@ -1341,7 +1430,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
new_state = KMemoryState::AliasCodeData;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
}
@@ -1687,22 +1776,22 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
-ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
+ResultCode KPageTable::LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size) {
return this->LockMemoryAndOpen(
- nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
- KMemoryAttribute::All, KMemoryAttribute::None,
+ out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
+ KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None,
static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
KMemoryPermission::KernelReadWrite),
KMemoryAttribute::Locked);
}
-ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
- return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
- KMemoryPermission::None, KMemoryAttribute::All,
- KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
- KMemoryAttribute::Locked, nullptr);
+ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size,
+ const KPageLinkedList& pg) {
+ return this->UnlockMemory(
+ addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
+ KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
}
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1734,9 +1823,7 @@ void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
VAddr addr{start};
while (addr < start + (num_pages * PageSize)) {
const PAddr paddr{GetPhysicalAddr(addr)};
- if (!paddr) {
- UNREACHABLE();
- }
+ ASSERT(paddr != 0);
page_linked_list.AddBlock(paddr, 1);
addr += PageSize;
}
@@ -1767,7 +1854,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
addr += size;
@@ -1798,7 +1885,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
case OperationType::ChangePermissionsAndRefresh:
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
return ResultSuccess;
}
@@ -1835,7 +1922,6 @@ VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
return code_region_start;
default:
UNREACHABLE();
- return {};
}
}
@@ -1871,7 +1957,6 @@ std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
return code_region_end - code_region_start;
default:
UNREACHABLE();
- return {};
}
}
@@ -2125,7 +2210,7 @@ ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_
// Check the page group.
if (pg != nullptr) {
- UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
+ R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
}
// 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:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
- ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
- ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
+ ResultCode LockForCodeMemory(KPageLinkedList* out, VAddr addr, std::size_t size);
+ ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageLinkedList& pg);
ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
KMemoryState state_mask, KMemoryState state,
KMemoryPermission perm_mask, KMemoryPermission perm,
@@ -178,6 +178,7 @@ private:
const KPageLinkedList* pg);
ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
+ bool IsValidPageGroup(const KPageLinkedList& pg, VAddr addr, size_t num_pages);
bool IsLockedByCurrentThread() const {
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) {
if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
session_ptr->ClientConnected(server.AcceptSession());
} else {
- UNREACHABLE();
+ ASSERT(false);
}
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
{
KScopedSchedulerLock lock{kernel};
thread->SetState(ThreadState::Runnable);
+
+ if (system.DebuggerEnabled()) {
+ thread->RequestSuspend(SuspendType::Debug);
+ }
}
}
} // Anonymous namespace
@@ -346,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
// 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
"object_id {} is too big! This probably means a recent service call "
"to {} needed to return a new interface!",
object_id, name);
- UNREACHABLE();
+ ASSERT(false);
return ResultSuccess; // Ignore error if asserts are off
}
if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
return strong_ptr->HandleSyncRequest(*this, context);
} else {
- UNREACHABLE();
+ ASSERT(false);
return ResultSuccess;
}
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
UNIMPLEMENTED();
break;
default:
- UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
+ ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break;
}
thread_type = type;
@@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
resource_limit_release_hint = false;
cpu_time = 0;
+ // Set debug context.
+ stack_top = user_stack_top;
+ argument = arg;
+
// Clear our stack parameters.
std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
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 {
Suspended, ///< Thread is waiting due to process suspension
};
+enum class StepState : u32 {
+ NotStepping, ///< Thread is not currently stepping
+ StepPending, ///< Thread will step when next scheduled
+ StepPerformed, ///< Thread has stepped, waiting to be scheduled again
+};
+
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -267,6 +273,14 @@ public:
void SetState(ThreadState state);
+ [[nodiscard]] StepState GetStepState() const {
+ return step_state;
+ }
+
+ void SetStepState(StepState state) {
+ step_state = state;
+ }
+
[[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
@@ -646,6 +660,14 @@ public:
void IfDummyThreadTryWait();
void IfDummyThreadEndWait();
+ [[nodiscard]] uintptr_t GetArgument() const {
+ return argument;
+ }
+
+ [[nodiscard]] VAddr GetUserStackTop() const {
+ return stack_top;
+ }
+
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
@@ -769,6 +791,7 @@ private:
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
ThreadType thread_type{};
+ StepState step_state{};
std::mutex dummy_wait_lock;
std::condition_variable dummy_wait_cv;
@@ -776,6 +799,8 @@ private:
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
+ uintptr_t argument;
+ VAddr stack_top;
public:
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 {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
- const auto [total_size, kernel_size] = memory_layout->GetTotalAndKernelMemorySizes();
+ const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
+ const auto total_size{sizes.first};
+ const auto kernel_size{sizes.second};
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -252,6 +254,7 @@ struct KernelCore::Impl {
core_id)
.IsSuccess());
suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
+ suspend_threads[core_id]->DisableDispatch();
}
}
@@ -1073,9 +1076,6 @@ void KernelCore::Suspend(bool in_suspention) {
impl->suspend_threads[core_id]->SetState(state);
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
ThreadWaitReasonForDebugging::Suspended);
- if (!should_suspend) {
- impl->suspend_threads[core_id]->DisableDispatch();
- }
}
}
}
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 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
@@ -627,6 +628,12 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
+
+ if (system.DebuggerEnabled()) {
+ auto* thread = system.Kernel().GetCurrentEmuThread();
+ system.GetDebugger().NotifyThreadStopped(thread);
+ thread->RequestSuspend(Kernel::SuspendType::Debug);
+ }
}
static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
@@ -1876,7 +1883,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
KScheduler::YieldToAnyThread(kernel);
} else {
// Nintendo does nothing at all if an otherwise invalid value is passed.
- UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
+ ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
}
@@ -2982,7 +2989,6 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
}
void Call(Core::System& system, u32 immediate) {
- system.ExitDynarmicProfile();
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
@@ -3007,8 +3013,6 @@ void Call(Core::System& system, u32 immediate) {
auto* host_context = thread->GetHostContext().get();
host_context->Rewind();
}
-
- system.EnterDynarmicProfile();
}
} // 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 {
}
void Controller::ExecuteInteractive() {
- UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void 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 {
}
void Error::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data!");
+ ASSERT_MSG(false, "Unexpected interactive applet data!");
}
void 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 {
}
void Auth::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data.");
+ ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void Auth::Execute() {
@@ -175,7 +175,7 @@ ResultCode PhotoViewer::GetStatus() const {
}
void PhotoViewer::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data.");
+ ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void 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 {
}
void MiiEdit::ExecuteInteractive() {
- UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void 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 {
}
void ProfileSelect::ExecuteInteractive() {
- UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void 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() {
InitializeBackground(applet_mode);
break;
default:
- UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode);
+ ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
break;
}
}
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() {
InitializeLobby();
break;
default:
- UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
+ ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
break;
}
}
@@ -320,7 +320,7 @@ void WebBrowser::Execute() {
ExecuteLobby();
break;
default:
- UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
+ ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
WebBrowserExit(WebExitReason::EndButtonPressed);
break;
}
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) {
case FileSys::SaveDataSpaceId::TemporaryStorage:
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
- UNREACHABLE();
+ ASSERT(false);
}
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) {
}
last_update_timestamp = shared_memory->gesture_lifo.timestamp;
+ UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
return false;
}
-void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
- GestureProperties& gesture,
+void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
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:
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
- void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
- f32 time_difference);
+ void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
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 @@
#include <algorithm>
#include <array>
+#include <chrono>
#include <cstring>
+
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -54,11 +56,22 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle
return npad_id && npad_type && device_index;
}
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
+ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle) {
const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
- const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+ if (!npad_id) {
+ return InvalidNpadId;
+ }
const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
- return npad_id && npad_type && device_index;
+ if (!device_index) {
+ return NpadDeviceIndexOutOfRange;
+ }
+ // This doesn't get validated on nnsdk
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+ if (!npad_type) {
+ return NpadInvalidHandle;
+ }
+ return ResultSuccess;
}
Controller_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) {
shared_memory->system_properties.raw = 0;
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
shared_memory->style_tag.fullkey.Assign(1);
@@ -156,6 +169,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
+ shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
shared_memory->style_tag.handheld.Assign(1);
@@ -168,16 +182,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory->applet_nfc_xcd.applet_footer.type =
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+ shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
@@ -196,6 +213,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
+ shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
shared_memory->style_tag.joycon_right.Assign(1);
@@ -203,6 +221,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
+ shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::GameCube:
shared_memory->style_tag.gamecube.Assign(1);
@@ -213,6 +232,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball:
shared_memory->style_tag.palma.Assign(1);
shared_memory->device_type.palma.Assign(1);
+ shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::NES:
shared_memory->style_tag.lark.Assign(1);
@@ -402,7 +422,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
libnx_state.connection_status.is_connected.Assign(1);
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::NES:
@@ -529,6 +549,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
+ // Clear previous state
+ sixaxis_fullkey_state = {};
+ sixaxis_handheld_state = {};
+ sixaxis_dual_left_state = {};
+ sixaxis_dual_right_state = {};
+ sixaxis_left_lifo_state = {};
+ sixaxis_right_lifo_state = {};
+
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
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
}
}
+ const auto set_motion_state = [&](SixAxisSensorState& state,
+ const Core::HID::ControllerMotion& hid_state) {
+ using namespace std::literals::chrono_literals;
+ static constexpr SixAxisSensorState default_motion_state = {
+ .delta_time = std::chrono::nanoseconds(5ms).count(),
+ .accel = {0, 0, -1.0f},
+ .orientation =
+ {
+ Common::Vec3f{1.0f, 0, 0},
+ Common::Vec3f{0, 1.0f, 0},
+ Common::Vec3f{0, 0, 1.0f},
+ },
+ .attribute = {1},
+ };
+ if (!controller.sixaxis_sensor_enabled) {
+ state = default_motion_state;
+ return;
+ }
+ if (!Settings::values.motion_enabled.GetValue()) {
+ state = default_motion_state;
+ return;
+ }
+ state.attribute.is_connected.Assign(1);
+ state.delta_time = std::chrono::nanoseconds(5ms).count();
+ state.accel = hid_state.accel;
+ state.gyro = hid_state.gyro;
+ state.rotation = hid_state.rotation;
+ state.orientation = hid_state.orientation;
+ };
+
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
- sixaxis_fullkey_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_fullkey_state.attribute.is_connected.Assign(1);
- sixaxis_fullkey_state.accel = motion_state[0].accel;
- sixaxis_fullkey_state.gyro = motion_state[0].gyro;
- sixaxis_fullkey_state.rotation = motion_state[0].rotation;
- sixaxis_fullkey_state.orientation = motion_state[0].orientation;
- }
+ case Core::HID::NpadStyleIndex::Pokeball:
+ set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
- sixaxis_handheld_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_handheld_state.attribute.is_connected.Assign(1);
- sixaxis_handheld_state.accel = motion_state[0].accel;
- sixaxis_handheld_state.gyro = motion_state[0].gyro;
- sixaxis_handheld_state.rotation = motion_state[0].rotation;
- sixaxis_handheld_state.orientation = motion_state[0].orientation;
- }
+ set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
- sixaxis_dual_left_state.attribute.raw = 0;
- sixaxis_dual_right_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- // Set motion for the left joycon
- sixaxis_dual_left_state.attribute.is_connected.Assign(1);
- sixaxis_dual_left_state.accel = motion_state[0].accel;
- sixaxis_dual_left_state.gyro = motion_state[0].gyro;
- sixaxis_dual_left_state.rotation = motion_state[0].rotation;
- sixaxis_dual_left_state.orientation = motion_state[0].orientation;
- }
- if (controller.sixaxis_sensor_enabled) {
- // Set motion for the right joycon
- sixaxis_dual_right_state.attribute.is_connected.Assign(1);
- sixaxis_dual_right_state.accel = motion_state[1].accel;
- sixaxis_dual_right_state.gyro = motion_state[1].gyro;
- sixaxis_dual_right_state.rotation = motion_state[1].rotation;
- sixaxis_dual_right_state.orientation = motion_state[1].orientation;
- }
+ set_motion_state(sixaxis_dual_left_state, motion_state[0]);
+ set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
- sixaxis_left_lifo_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
- sixaxis_left_lifo_state.accel = motion_state[0].accel;
- sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
- sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
- sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
- }
+ set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
- sixaxis_right_lifo_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
- sixaxis_right_lifo_state.accel = motion_state[1].accel;
- sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
- sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
- sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
- }
+ set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
break;
default:
break;
@@ -676,6 +691,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
}
void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
+ if (joy_hold_type != NpadJoyHoldType::Horizontal &&
+ joy_hold_type != NpadJoyHoldType::Vertical) {
+ LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
+ joy_hold_type);
+ return;
+ }
hold_type = joy_hold_type;
}
@@ -699,11 +720,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode) {
+ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
+ NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
@@ -712,7 +734,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
}
if (!controller.device->IsConnected()) {
- return;
+ return ResultSuccess;
}
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
@@ -721,34 +743,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
- return;
+ return ResultSuccess;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
- return;
+ return ResultSuccess;
}
- return;
+ return ResultSuccess;
}
// This is for NpadJoyAssignmentMode::Single
// Only JoyconDual get affected by this function
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
- return;
+ return ResultSuccess;
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
- return;
+ return ResultSuccess;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
- return;
+ return ResultSuccess;
}
// 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
controller_2.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
+ return ResultSuccess;
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -833,7 +856,7 @@ void Controller_NPad::VibrateController(
}
if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
- UNREACHABLE_MSG("DeviceIndex should never be None!");
+ ASSERT_MSG(false, "DeviceIndex should never be None!");
return;
}
@@ -961,10 +984,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
InitNewlyAddedController(npad_id);
}
-void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
+ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
@@ -981,6 +1004,12 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
shared_memory->device_type.raw = 0;
shared_memory->system_properties.raw = 0;
shared_memory->button_properties.raw = 0;
+ shared_memory->sixaxis_fullkey_properties.raw = 0;
+ shared_memory->sixaxis_handheld_properties.raw = 0;
+ shared_memory->sixaxis_dual_left_properties.raw = 0;
+ shared_memory->sixaxis_dual_right_properties.raw = 0;
+ shared_memory->sixaxis_left_properties.raw = 0;
+ shared_memory->sixaxis_right_properties.raw = 0;
shared_memory->battery_level_dual = 0;
shared_memory->battery_level_left = 0;
shared_memory->battery_level_right = 0;
@@ -1001,346 +1030,268 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
WriteEmptyEntry(shared_memory);
+ return ResultSuccess;
}
-
-ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
- break;
- }
- controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.gyroscope_zero_drift_mode = drift_mode;
return ResultSuccess;
}
-ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode& drift_mode) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
- break;
- }
- drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ drift_mode = sixaxis.gyroscope_zero_drift_mode;
return ResultSuccess;
}
-ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool& is_at_rest) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ResultCode Controller_NPad::IsSixAxisSensorAtRest(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
+
const auto& controller = GetControllerFromHandle(sixaxis_handle);
is_at_rest = controller.sixaxis_at_rest;
return ResultSuccess;
}
ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
- Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- // We don't support joycon firmware updates
- is_firmware_available = false;
+ const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
return ResultSuccess;
}
-ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.unaltered_passtrough = is_enabled;
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ is_enabled = sixaxis.unaltered_passtrough;
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ // TODO: Request this data to the controller. On error return 0xd8ca
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ calibration = sixaxis.calibration;
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorIcInformation& ic_information) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ // TODO: Request this data to the controller. On error return 0xd8ca
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ ic_information = sixaxis.ic_information;
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ sixaxis_properties.is_newly_assigned.Assign(0);
+
+ return ResultSuccess;
+}
+
+ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
return ResultSuccess;
}
ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
- Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
- break;
- }
- is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ is_fusion_enabled = sixaxis.is_fusion_enabled;
return ResultSuccess;
}
-ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool is_fusion_enabled) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ResultCode Controller_NPad::SetSixAxisFusionEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
- break;
- }
- controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.is_fusion_enabled = is_fusion_enabled;
return ResultSuccess;
}
ResultCode Controller_NPad::SetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
+
const auto param1 = sixaxis_fusion_parameters.parameter1;
if (param1 < 0.0f || param1 > 1.0f) {
return InvalidSixAxisFusionRange;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
- break;
- }
- controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.fusion = sixaxis_fusion_parameters;
return ResultSuccess;
}
ResultCode Controller_NPad::GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return NpadInvalidHandle;
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- const auto& controller = GetControllerFromHandle(sixaxis_handle);
- switch (sixaxis_handle.npad_type) {
- case Core::HID::NpadStyleIndex::ProController:
- parameters = controller.sixaxis_fullkey.fusion;
- break;
- case Core::HID::NpadStyleIndex::Handheld:
- parameters = controller.sixaxis_handheld.fusion;
- break;
- case Core::HID::NpadStyleIndex::JoyconDual:
- case Core::HID::NpadStyleIndex::GameCube:
- case Core::HID::NpadStyleIndex::Pokeball:
- if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
- parameters = controller.sixaxis_dual_left.fusion;
- break;
- }
- parameters = controller.sixaxis_dual_right.fusion;
- break;
- case Core::HID::NpadStyleIndex::JoyconLeft:
- parameters = controller.sixaxis_left.fusion;
- break;
- case Core::HID::NpadStyleIndex::JoyconRight:
- parameters = controller.sixaxis_right.fusion;
- break;
- default:
- LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
- return NpadInvalidHandle;
- }
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ parameters = sixaxis.fusion;
return ResultSuccess;
}
-void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return;
+ return InvalidNpadId;
}
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
- const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
- const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
- bool merge_controllers = false;
+ auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
+ auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
- // If the controllers at both npad indices form a pair of left and right joycons, merge them.
- // Otherwise, do nothing.
+ // Simplify this code by converting dualjoycon with only a side connected to single joycons
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
+ if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
+ controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
+ }
+ if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
+ controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
+ }
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
+ if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
+ }
+ if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
+ }
+ }
+
+ // Invalid merge errors
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
+ return NpadIsDualJoycon;
+ }
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
+ return NpadIsSameType;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
- controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
- !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
- controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
- !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
- !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
- controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
-
- if (merge_controllers) {
- // Disconnect the joycon at the second id and connect the dual joycon at the first index.
- DisconnectNpad(npad_id_2);
- controller_1.is_dual_left_connected = true;
- controller_1.is_dual_right_connected = true;
- AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
- return;
+ return NpadIsSameType;
+ }
+
+ // These exceptions are handled as if they where dual joycon
+ if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
+ return NpadIsDualJoycon;
+ }
+ if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
+ return NpadIsDualJoycon;
}
- LOG_WARNING(Service_HID,
- "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
- "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
- npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
- controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
- controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
- controller_2.is_dual_right_connected);
+
+ // Disconnect the joycon at the second id and connect the dual joycon at the first index.
+ DisconnectNpad(npad_id_2);
+ controller_1.is_dual_left_connected = true;
+ controller_1.is_dual_right_connected = true;
+ AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ return ResultSuccess;
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1353,17 +1304,17 @@ void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false;
}
-bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return false;
+ return InvalidNpadId;
}
if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
npad_id_2 == Core::HID::NpadIdType::Other) {
- return true;
+ return ResultSuccess;
}
const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
@@ -1373,46 +1324,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
const auto is_connected_2 = controller_2->IsConnected();
if (!IsControllerSupported(type_index_1) && is_connected_1) {
- return false;
+ return NpadNotConnected;
}
if (!IsControllerSupported(type_index_2) && is_connected_2) {
- return false;
+ return NpadNotConnected;
}
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
- return true;
+ return ResultSuccess;
}
-Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
+ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
+ Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return Core::HID::LedPattern{0, 0, 0, 0};
+ return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id).device;
- return controller->GetLedPattern();
+ pattern = controller->GetLedPattern();
+ return ResultSuccess;
}
-bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
- Core::HID::NpadIdType npad_id) const {
+ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
+ Core::HID::NpadIdType npad_id, bool& is_valid) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- // Return the default value
- return false;
+ return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id);
- return controller.unintended_home_button_input_protection;
+ is_valid = controller.unintended_home_button_input_protection;
+ return ResultSuccess;
}
-void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id) {
+ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
+ bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
controller.unintended_home_button_input_protection = is_protection_enabled;
+ return ResultSuccess;
}
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1550,4 +1504,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
return controller_data[npad_index];
}
+Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.shared_memory->sixaxis_handheld_properties;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.shared_memory->sixaxis_dual_left_properties;
+ }
+ return controller.shared_memory->sixaxis_dual_right_properties;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.shared_memory->sixaxis_left_properties;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.shared_memory->sixaxis_right_properties;
+ default:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ }
+}
+
+const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.shared_memory->sixaxis_handheld_properties;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.shared_memory->sixaxis_dual_left_properties;
+ }
+ return controller.shared_memory->sixaxis_dual_right_properties;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.shared_memory->sixaxis_left_properties;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.shared_memory->sixaxis_right_properties;
+ default:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ }
+}
+
+Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.sixaxis_fullkey;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.sixaxis_handheld;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.sixaxis_dual_left;
+ }
+ return controller.sixaxis_dual_right;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.sixaxis_left;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.sixaxis_right;
+ default:
+ return controller.sixaxis_unknown;
+ }
+}
+
+const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.sixaxis_fullkey;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.sixaxis_handheld;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.sixaxis_dual_left;
+ }
+ return controller.sixaxis_dual_right;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.sixaxis_left;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.sixaxis_right;
+ default:
+ return controller.sixaxis_unknown;
+ }
+}
+
} // 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:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode);
+ ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -141,50 +141,65 @@ public:
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
bool connected);
- void DisconnectNpad(Core::HID::NpadIdType npad_id);
+ ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
- ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode drift_mode);
- ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
GyroscopeZeroDriftMode& drift_mode) const;
- ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_at_rest) const;
ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
- Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
- ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
+ ResultCode EnableSixAxisSensorUnalteredPassthrough(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
+ ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
+ ResultCode LoadSixAxisSensorCalibrationParameter(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
+ ResultCode GetSixAxisSensorIcInformation(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorIcInformation& ic_information) const;
+ ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle);
+ ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool sixaxis_status);
- ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool& is_fusion_enabled) const;
- ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
+ ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
bool is_fusion_enabled);
ResultCode SetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
ResultCode GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters& parameters) const;
- Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
- bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
- void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id);
+ ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
+ ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
+ bool& is_enabled) const;
+ ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
- void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
- bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
- static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
+ static ResultCode VerifyValidSixAxisSensorHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle);
private:
static constexpr std::size_t NPAD_COUNT = 10;
@@ -451,9 +466,13 @@ private:
NpadLuciaType lucia_type{};
NpadLagonType lagon_type{};
NpadLagerType lager_type{};
- // FW 13.x Investigate there is some sort of bitflag related to joycons
- INSERT_PADDING_BYTES(0x4);
- INSERT_PADDING_BYTES(0xc08); // Unknown
+ Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_left_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_right_properties;
+ INSERT_PADDING_BYTES(0xc06); // Unknown
};
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
@@ -465,7 +484,10 @@ private:
struct SixaxisParameters {
bool is_fusion_enabled{true};
+ bool unaltered_passtrough{false};
Core::HID::SixAxisSensorFusionParameters fusion{};
+ Core::HID::SixAxisSensorCalibrationParameter calibration{};
+ Core::HID::SixAxisSensorIcInformation ic_information{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
};
@@ -491,6 +513,7 @@ private:
SixaxisParameters sixaxis_dual_right{};
SixaxisParameters sixaxis_left{};
SixaxisParameters sixaxis_right{};
+ SixaxisParameters sixaxis_unknown{};
// Current pad state
NPadGenericState npad_pad_state{};
@@ -522,6 +545,14 @@ private:
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
+ Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& device_handle);
+ const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& device_handle) const;
+ SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
+ const SixaxisParameters& GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& device_handle) const;
+
std::atomic<u64> press_state{};
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
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
- finger.position = current_touch.position;
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
@@ -61,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
if (!finger.pressed && current_touch.pressed) {
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
+ finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
+ continue;
}
+
+ // Only update position if touch is not on a special frame
+ finger.position = current_touch.position;
}
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 @@
namespace Service::HID {
constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
+constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
+constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
+constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
+constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
} // 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 {
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
-// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed
-constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz)
+constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
IAppletResource::IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_)
@@ -258,12 +257,12 @@ Hid::Hid(Core::System& system_)
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
- {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
- {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
+ {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
+ {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
- {87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
- {88, nullptr, "GetSixAxisSensorIcInformation"},
- {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
+ {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
+ {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
+ {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -695,11 +694,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
rb.Push(result1);
return;
}
- if (result2.IsError()) {
- rb.Push(result2);
- return;
- }
- rb.Push(ResultSuccess);
+ rb.Push(result2);
}
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -822,6 +817,144 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
rb.Push(is_firmware_available);
}
+void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enabled;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
+ parameters.sixaxis_handle, parameters.enabled);
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
+ "applet_resource_user_id={}",
+ parameters.enabled, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ bool is_unaltered_sisxaxis_enabled{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
+ parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(is_unaltered_sisxaxis_enabled);
+}
+
+void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ Core::HID::SixAxisSensorCalibrationParameter calibration{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(calibration);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ Core::HID::SixAxisSensorIcInformation ic_information{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(ic_information);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
struct Parameters {
@@ -949,27 +1082,29 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .DisconnectNpad(parameters.npad_id);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.DisconnectNpad(parameters.npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
+ Core::HID::LedPattern pattern{0, 0, 0, 0};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.GetLedPattern(npad_id, pattern);
+
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetLedPattern(npad_id)
- .raw);
+ rb.Push(result);
+ rb.Push(pattern.raw);
}
void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
@@ -1029,15 +1164,16 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
@@ -1052,16 +1188,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
@@ -1075,14 +1211,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetNpadMode(parameters.npad_id, {},
+ Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1091,14 +1228,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
@@ -1158,19 +1295,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SwapNpadAssignment(npad_id_1, npad_id_2);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- if (res) {
- rb.Push(ResultSuccess);
- } else {
- LOG_ERROR(Service_HID, "Npads are not connected!");
- rb.Push(NpadNotConnected);
- }
+ rb.Push(result);
}
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
@@ -1184,13 +1316,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_enabled = false;
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
+
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
+ rb.Push(result);
+ rb.Push(is_enabled);
}
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
@@ -1205,9 +1341,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetUnintendedHomeButtonInputProtectionEnabled(
- parameters.unintended_home_button_input_protection, parameters.npad_id);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
+ parameters.unintended_home_button_input_protection, parameters.npad_id);
LOG_WARNING(Service_HID,
"(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
@@ -1216,7 +1352,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
@@ -1305,7 +1441,7 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
break;
case Core::HID::DeviceIndex::None:
default:
- UNREACHABLE_MSG("DeviceIndex should never be None!");
+ ASSERT_MSG(false, "DeviceIndex should never be None!");
vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
break;
}
@@ -1378,6 +1514,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
+ // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
+ // by converting it to a bool
Settings::values.vibration_enabled.SetValue(can_vibrate);
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -1389,9 +1527,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
+ // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
+ const auto is_enabled = Settings::values.vibration_enabled.GetValue();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(Settings::values.vibration_enabled.GetValue());
+ rb.Push(is_enabled);
}
void 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:
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
+ void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
+ void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
+ void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
+ void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
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 @@
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
@@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_IRS, "called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
@@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ PackedMomentProcessorConfig processor_config;
+ };
+ static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ PackedClusteringProcessorConfig processor_config;
+ };
+ static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ PackedImageTransferProcessorConfig processor_config;
+ u32 transfer_memory_size;
+ };
+ static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
@@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
+ "applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
+ processor_config.required_mcu_version.major,
+ processor_config.required_mcu_version.minor, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
+
+ if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
+ npad_id != Core::HID::NpadIdType::Handheld) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidNpadId);
+ return;
+ }
+
+ IrCameraHandle camera_handle{
+ .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
+ .npad_type = Core::HID::NpadStyleIndex::None,
+ };
+
+ LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
+ npad_id, camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.PushRaw<u32>(device_handle);
+ rb.PushRaw(camera_handle);
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
+ processor_config.required_mcu_version.minor, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
+ const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
+ camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
+ mcu_version.minor);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ PackedFunctionLevel function_level;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ PackedImageTransferProcessorExConfig processor_config;
+ u64 transfer_memory_size;
+ };
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
+ "applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
+ processor_config.required_mcu_version.major,
+ processor_config.required_mcu_version.minor, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ PackedFunctionLevel function_level;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
+ parameters.function_level.function_level, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
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 @@
#pragma once
+#include "core/hid/hid_types.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -17,6 +18,235 @@ public:
~IRS() override;
private:
+ // This is nn::irsensor::IrCameraStatus
+ enum IrCameraStatus : u32 {
+ Available,
+ Unsupported,
+ Unconnected,
+ };
+
+ // This is nn::irsensor::IrCameraInternalStatus
+ enum IrCameraInternalStatus : u32 {
+ Stopped,
+ FirmwareUpdateNeeded,
+ Unkown2,
+ Unkown3,
+ Unkown4,
+ FirmwareVersionRequested,
+ FirmwareVersionIsInvalid,
+ Ready,
+ Setting,
+ };
+
+ // This is nn::irsensor::detail::StatusManager::IrSensorMode
+ enum IrSensorMode : u64 {
+ None,
+ MomentProcessor,
+ ClusteringProcessor,
+ ImageTransferProcessor,
+ PointingProcessorMarker,
+ TeraPluginProcessor,
+ IrLedProcessor,
+ };
+
+ // This is nn::irsensor::ImageProcessorStatus
+ enum ImageProcessorStatus : u8 {
+ stopped,
+ running,
+ };
+
+ // This is nn::irsensor::ImageTransferProcessorFormat
+ enum ImageTransferProcessorFormat : u8 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+ };
+
+ // This is nn::irsensor::AdaptiveClusteringMode
+ enum AdaptiveClusteringMode : u8 {
+ StaticFov,
+ DynamicFov,
+ };
+
+ // This is nn::irsensor::AdaptiveClusteringTargetDistance
+ enum AdaptiveClusteringTargetDistance : u8 {
+ Near,
+ Middle,
+ Far,
+ };
+
+ // This is nn::irsensor::IrsHandAnalysisMode
+ enum IrsHandAnalysisMode : u8 {
+ Silhouette,
+ Image,
+ SilhoueteAndImage,
+ SilhuetteOnly,
+ };
+
+ // This is nn::irsensor::IrSensorFunctionLevel
+ enum IrSensorFunctionLevel : u8 {
+ unknown0,
+ unknown1,
+ unknown2,
+ unknown3,
+ unknown4,
+ };
+
+ // This is nn::irsensor::IrCameraHandle
+ struct IrCameraHandle {
+ u8 npad_id{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+ INSERT_PADDING_BYTES(2);
+ };
+ static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
+
+ struct IrsRect {
+ s16 x;
+ s16 y;
+ s16 width;
+ s16 height;
+ };
+
+ // This is nn::irsensor::PackedMcuVersion
+ struct PackedMcuVersion {
+ u16 major;
+ u16 minor;
+ };
+ static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
+
+ // This is nn::irsensor::MomentProcessorConfig
+ struct MomentProcessorConfig {
+ u64 exposire_time;
+ u8 light_target;
+ u8 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(7);
+ IrsRect window_of_interest;
+ u8 preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(5);
+ };
+ static_assert(sizeof(MomentProcessorConfig) == 0x28,
+ "MomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedMomentProcessorConfig
+ struct PackedMomentProcessorConfig {
+ u64 exposire_time;
+ u8 light_target;
+ u8 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(5);
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u8 preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(2);
+ };
+ static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
+ "PackedMomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ClusteringProcessorConfig
+ struct ClusteringProcessorConfig {
+ u64 exposire_time;
+ u32 light_target;
+ u32 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(7);
+ IrsRect window_of_interest;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ u8 is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
+ "ClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedClusteringProcessorConfig
+ struct PackedClusteringProcessorConfig {
+ u64 exposire_time;
+ u8 light_target;
+ u8 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(5);
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ u8 is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(2);
+ };
+ static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
+ "PackedClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedImageTransferProcessorConfig
+ struct PackedImageTransferProcessorConfig {
+ u64 exposire_time;
+ u8 light_target;
+ u8 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(5);
+ PackedMcuVersion required_mcu_version;
+ u8 format;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
+ "PackedImageTransferProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedTeraPluginProcessorConfig
+ struct PackedTeraPluginProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 mode;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
+ "PackedTeraPluginProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedPointingProcessorConfig
+ struct PackedPointingProcessorConfig {
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ };
+ static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
+ "PackedPointingProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::PackedFunctionLevel
+ struct PackedFunctionLevel {
+ IrSensorFunctionLevel function_level;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
+
+ // This is nn::irsensor::PackedImageTransferProcessorExConfig
+ struct PackedImageTransferProcessorExConfig {
+ u64 exposire_time;
+ u8 light_target;
+ u8 gain;
+ u8 is_negative_used;
+ INSERT_PADDING_BYTES(5);
+ PackedMcuVersion required_mcu_version;
+ ImageTransferProcessorFormat origin_format;
+ ImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ u8 is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
+ "PackedImageTransferProcessorExConfig is an invalid size");
+
+ // This is nn::irsensor::PackedIrLedProcessorConfig
+ struct PackedIrLedProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 light_target;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
+ "PackedIrLedProcessorConfig is an invalid size");
+
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -35,8 +265,6 @@ private:
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
-
- const u32 device_handle{0xABCD};
};
class 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 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/div_ceil.h"
+#include "common/elf.h"
#include "common/logging/log.h"
#include "core/hle/service/jit/jit_context.h"
#include "core/memory.h"
+using namespace Common::ELF;
+
namespace Service::JIT {
constexpr std::array<u8, 8> SVC0_ARM64 = {
@@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{
"_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
};
-struct Elf64_Dyn {
- u64 d_tag;
- u64 d_un;
-};
-
-struct Elf64_Rela {
- u64 r_offset;
- u64 r_info;
- s64 r_addend;
-};
-
-static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
- return static_cast<u32>(rela->r_info);
-}
-
-constexpr int DT_RELA = 7; /* Address of Rela relocs */
-constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
-constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
-
constexpr size_t STACK_ALIGN = 16;
class JITContextImpl;
@@ -206,17 +190,17 @@ public:
if (!dyn.d_tag) {
break;
}
- if (dyn.d_tag == DT_RELA) {
- rela_dyn = dyn.d_un;
+ if (dyn.d_tag == ElfDtRela) {
+ rela_dyn = dyn.d_un.d_ptr;
}
- if (dyn.d_tag == DT_RELASZ) {
- num_rela = dyn.d_un / sizeof(Elf64_Rela);
+ if (dyn.d_tag == ElfDtRelasz) {
+ num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
}
}
for (size_t i = 0; i < num_rela; i++) {
const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
- if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
+ if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
continue;
}
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:
}
if (!succeeded) {
- UNREACHABLE_MSG("Out of address space!");
+ ASSERT_MSG(false, "Out of address space!");
return Kernel::ResultOutOfMemory;
}
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
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
- UNREACHABLE();
+ ASSERT(false);
break;
}
}
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() {
return syncpoint_id;
}
}
- UNREACHABLE_MSG("No more available syncpoints!");
+ ASSERT_MSG(false, "No more available syncpoints!");
return {};
}
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,
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
- // If the front buffer is still being tracked, update its slot state
- if (core->StillTracking(*front)) {
- slots[slot].acquire_called = true;
- slots[slot].needs_cleanup_on_release = false;
- slots[slot].buffer_state = BufferState::Acquired;
- slots[slot].fence = Fence::NoFence();
- }
-
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
// avoid unnecessarily remapping this buffer on the consumer side.
if (out_buffer->acquire_called) {
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
++current;
}
- if (slots[slot].buffer_state == BufferState::Acquired) {
- slots[slot].fence = release_fence;
- slots[slot].buffer_state = BufferState::Free;
-
- listener = core->connected_producer_listener;
-
- LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
- } else if (slots[slot].needs_cleanup_on_release) {
- LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
- slots[slot].buffer_state);
+ slots[slot].buffer_state = BufferState::Free;
- slots[slot].needs_cleanup_on_release = false;
+ listener = core->connected_producer_listener;
- return Status::StaleBufferSlot;
- } else {
- LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
- slot, slots[slot].buffer_state);
-
- return Status::BadValue;
- }
+ LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
core->SignalDequeueCondition();
}
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) {
slots[slot].graphic_buffer.reset();
- if (slots[slot].buffer_state == BufferState::Acquired) {
- slots[slot].needs_cleanup_on_release = true;
- }
-
slots[slot].buffer_state = BufferState::Free;
slots[slot].frame_number = UINT32_MAX;
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) {
value = core->consumer_usage_bit;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
return Status::BadValue;
}
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 {
u64 frame_number{};
Fence fence;
bool acquire_called{};
- bool needs_cleanup_on_release{};
bool attached_by_consumer{};
bool is_preallocated{};
};
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,
}
ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
- UNREACHABLE();
+ UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
- UNREACHABLE();
+ UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
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 {
FileSys::VirtualFile& vfs_file) {
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
location_name, vfs_file) != ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -155,7 +155,7 @@ struct TimeManager::Impl final {
} else {
if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
}
@@ -170,7 +170,7 @@ struct TimeManager::Impl final {
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -183,7 +183,7 @@ struct TimeManager::Impl final {
Clock::SteadyClockTimePoint steady_clock_time_point) {
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
system_, is_automatic_correction_enabled) != ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -203,7 +203,7 @@ struct TimeManager::Impl final {
if (GetStandardLocalSystemClockCore()
.SetCurrentTime(system_, timespan.ToSeconds())
.IsError()) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
}
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) {
}
}
-static constexpr int GetMonthLength(bool is_leap_year, int month) {
- constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
- 31, 31, 30, 31, 30, 31};
+static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
+ constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
}
@@ -280,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
break;
}
default:
- UNREACHABLE();
+ ASSERT(false);
}
return value + rule.transition_time + offset;
}
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 @@
#include <memory>
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/elf.h"
#include "common/logging/log.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_page_table.h"
@@ -13,159 +14,7 @@
#include "core/loader/elf.h"
#include "core/memory.h"
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ELF Header Constants
-
-// File type
-enum ElfType {
- ET_NONE = 0,
- ET_REL = 1,
- ET_EXEC = 2,
- ET_DYN = 3,
- ET_CORE = 4,
- ET_LOPROC = 0xFF00,
- ET_HIPROC = 0xFFFF,
-};
-
-// Machine/Architecture
-enum ElfMachine {
- EM_NONE = 0,
- EM_M32 = 1,
- EM_SPARC = 2,
- EM_386 = 3,
- EM_68K = 4,
- EM_88K = 5,
- EM_860 = 7,
- EM_MIPS = 8
-};
-
-// File version
-#define EV_NONE 0
-#define EV_CURRENT 1
-
-// Identification index
-#define EI_MAG0 0
-#define EI_MAG1 1
-#define EI_MAG2 2
-#define EI_MAG3 3
-#define EI_CLASS 4
-#define EI_DATA 5
-#define EI_VERSION 6
-#define EI_PAD 7
-#define EI_NIDENT 16
-
-// Sections constants
-
-// Section types
-#define SHT_NULL 0
-#define SHT_PROGBITS 1
-#define SHT_SYMTAB 2
-#define SHT_STRTAB 3
-#define SHT_RELA 4
-#define SHT_HASH 5
-#define SHT_DYNAMIC 6
-#define SHT_NOTE 7
-#define SHT_NOBITS 8
-#define SHT_REL 9
-#define SHT_SHLIB 10
-#define SHT_DYNSYM 11
-#define SHT_LOPROC 0x70000000
-#define SHT_HIPROC 0x7FFFFFFF
-#define SHT_LOUSER 0x80000000
-#define SHT_HIUSER 0xFFFFFFFF
-
-// Section flags
-enum ElfSectionFlags {
- SHF_WRITE = 0x1,
- SHF_ALLOC = 0x2,
- SHF_EXECINSTR = 0x4,
- SHF_MASKPROC = 0xF0000000,
-};
-
-// Segment types
-#define PT_NULL 0
-#define PT_LOAD 1
-#define PT_DYNAMIC 2
-#define PT_INTERP 3
-#define PT_NOTE 4
-#define PT_SHLIB 5
-#define PT_PHDR 6
-#define PT_LOPROC 0x70000000
-#define PT_HIPROC 0x7FFFFFFF
-
-// Segment flags
-#define PF_X 0x1
-#define PF_W 0x2
-#define PF_R 0x4
-#define PF_MASKPROC 0xF0000000
-
-typedef unsigned int Elf32_Addr;
-typedef unsigned short Elf32_Half;
-typedef unsigned int Elf32_Off;
-typedef signed int Elf32_Sword;
-typedef unsigned int Elf32_Word;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ELF file header
-
-struct Elf32_Ehdr {
- unsigned char e_ident[EI_NIDENT];
- Elf32_Half e_type;
- Elf32_Half e_machine;
- Elf32_Word e_version;
- Elf32_Addr e_entry;
- Elf32_Off e_phoff;
- Elf32_Off e_shoff;
- Elf32_Word e_flags;
- Elf32_Half e_ehsize;
- Elf32_Half e_phentsize;
- Elf32_Half e_phnum;
- Elf32_Half e_shentsize;
- Elf32_Half e_shnum;
- Elf32_Half e_shstrndx;
-};
-
-// Section header
-struct Elf32_Shdr {
- Elf32_Word sh_name;
- Elf32_Word sh_type;
- Elf32_Word sh_flags;
- Elf32_Addr sh_addr;
- Elf32_Off sh_offset;
- Elf32_Word sh_size;
- Elf32_Word sh_link;
- Elf32_Word sh_info;
- Elf32_Word sh_addralign;
- Elf32_Word sh_entsize;
-};
-
-// Segment header
-struct Elf32_Phdr {
- Elf32_Word p_type;
- Elf32_Off p_offset;
- Elf32_Addr p_vaddr;
- Elf32_Addr p_paddr;
- Elf32_Word p_filesz;
- Elf32_Word p_memsz;
- Elf32_Word p_flags;
- Elf32_Word p_align;
-};
-
-// Symbol table entry
-struct Elf32_Sym {
- Elf32_Word st_name;
- Elf32_Addr st_value;
- Elf32_Word st_size;
- unsigned char st_info;
- unsigned char st_other;
- Elf32_Half st_shndx;
-};
-
-// Relocation entries
-struct Elf32_Rel {
- Elf32_Addr r_offset;
- Elf32_Word r_info;
-};
+using namespace Common::ELF;
////////////////////////////////////////////////////////////////////////////////////////////////////
// ElfReader class
@@ -193,11 +42,11 @@ public:
}
// Quick accessors
- ElfType GetType() const {
- return (ElfType)(header->e_type);
+ u16 GetType() const {
+ return header->e_type;
}
- ElfMachine GetMachine() const {
- return (ElfMachine)(header->e_machine);
+ u16 GetMachine() const {
+ return header->e_machine;
}
VAddr GetEntryPoint() const {
return entryPoint;
@@ -220,13 +69,13 @@ public:
const u8* GetSectionDataPtr(int section) const {
if (section < 0 || section >= header->e_shnum)
return nullptr;
- if (sections[section].sh_type != SHT_NOBITS)
+ if (sections[section].sh_type != ElfShtNobits)
return GetPtr(sections[section].sh_offset);
else
return nullptr;
}
bool IsCodeSection(int section) const {
- return sections[section].sh_type == SHT_PROGBITS;
+ return sections[section].sh_type == ElfShtProgBits;
}
const u8* GetSegmentPtr(int segment) {
return GetPtr(segments[segment].p_offset);
@@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) {
}
const char* ElfReader::GetSectionName(int section) const {
- if (sections[section].sh_type == SHT_NULL)
+ if (sections[section].sh_type == ElfShtNull)
return nullptr;
int name_offset = sections[section].sh_name;
@@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
- relocate = (header->e_type != ET_EXEC);
+ relocate = (header->e_type != ElfTypeExec);
if (relocate) {
LOG_DEBUG(Loader, "Relocatable module");
@@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
u64 total_image_size = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
- if (p->p_type == PT_LOAD) {
+ if (p->p_type == ElfPtLoad) {
total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
}
}
@@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
p->p_vaddr, p->p_filesz, p->p_memsz);
- if (p->p_type == PT_LOAD) {
+ if (p->p_type == ElfPtLoad) {
Kernel::CodeSet::Segment* codeset_segment;
- u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
- if (permission_flags == (PF_R | PF_X)) {
+ u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
+ if (permission_flags == (ElfPfRead | ElfPfExec)) {
codeset_segment = &codeset.CodeSegment();
- } else if (permission_flags == (PF_R)) {
+ } else if (permission_flags == (ElfPfRead)) {
codeset_segment = &codeset.RODataSegment();
- } else if (permission_flags == (PF_R | PF_W)) {
+ } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
codeset_segment = &codeset.DataSegment();
} else {
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::
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
- std::vector<u8> pi_header;
- pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
- reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
- pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(),
- program_image.data() + program_image.size());
+ std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
+ std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
+ std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
+ program_image.size());
pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
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 {
return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
}
+bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
+ VAddr end = base + size;
+ VAddr page = Common::AlignDown(base, PAGE_SIZE);
+
+ for (; page < end; page += PAGE_SIZE) {
+ if (!IsValidVirtualAddress(page)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
u8* Memory::GetPointer(VAddr vaddr) {
return impl->GetPointer(vaddr);
}
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:
[[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
/**
+ * Checks whether or not the supplied range of addresses are all valid
+ * virtual addresses for the current process.
+ *
+ * @param base The address to begin checking.
+ * @param size The amount of bytes to check.
+ *
+ * @returns True if all bytes in the given range are valid, false otherwise.
+ */
+ [[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
+
+ /**
* Gets a pointer to the given address.
*
* @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) {
return memory.Read64(addr);
default:
UNREACHABLE();
- return 0;
}
}
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()
-Werror
-Werror=conversion
-Werror=ignored-qualifiers
- -Werror=shadow
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
-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 @@
namespace InputCommon {
namespace {
-std::string GetGUID(SDL_Joystick* joystick) {
+Common::UUID GetGUID(SDL_Joystick* joystick) {
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
- char guid_str[33];
- SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
- return guid_str;
+ std::array<u8, 16> data{};
+ std::memcpy(data.data(), guid.data, sizeof(data));
+ return Common::UUID{data};
}
} // Anonymous namespace
@@ -31,9 +31,9 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
class SDLJoystick {
public:
- SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
+ SDLJoystick(Common::UUID guid_, int port_, SDL_Joystick* joystick,
SDL_GameController* game_controller)
- : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
+ : guid{guid_}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
sdl_controller{game_controller, &SDL_GameControllerClose} {
EnableMotion();
}
@@ -120,7 +120,7 @@ public:
*/
const PadIdentifier GetPadIdentifier() const {
return {
- .guid = Common::UUID{guid},
+ .guid = guid,
.port = static_cast<std::size_t>(port),
.pad = 0,
};
@@ -129,7 +129,7 @@ public:
/**
* The guid of the joystick
*/
- const std::string& GetGUID() const {
+ const Common::UUID& GetGUID() const {
return guid;
}
@@ -228,7 +228,7 @@ public:
}
private:
- std::string guid;
+ Common::UUID guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
@@ -240,7 +240,7 @@ private:
BasicMotion motion;
};
-std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
+std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const Common::UUID& guid, int port) {
std::scoped_lock lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
@@ -259,9 +259,13 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string&
return joystick_map[guid].emplace_back(std::move(joystick));
}
+std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
+ return GetSDLJoystickByGUID(Common::UUID{guid}, port);
+}
+
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl_id) {
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
- const std::string guid = GetGUID(sdl_joystick);
+ const auto guid = GetGUID(sdl_joystick);
std::scoped_lock lock{joystick_map_mutex};
const auto map_it = joystick_map.find(guid);
@@ -295,7 +299,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
return;
}
- const std::string guid = GetGUID(sdl_joystick);
+ const auto guid = GetGUID(sdl_joystick);
std::scoped_lock lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
@@ -324,7 +328,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
}
void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
- const std::string guid = GetGUID(sdl_joystick);
+ const auto guid = GetGUID(sdl_joystick);
std::scoped_lock lock{joystick_map_mutex};
// 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
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
+ SendVibrations();
std::this_thread::sleep_for(1ms);
}
});
@@ -469,7 +474,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
devices.emplace_back(Common::ParamPackage{
{"engine", GetEngineName()},
{"display", std::move(name)},
- {"guid", joystick->GetGUID()},
+ {"guid", joystick->GetGUID().RawString()},
{"port", std::to_string(joystick->GetPort())},
});
if (joystick->IsJoyconLeft()) {
@@ -492,8 +497,8 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
devices.emplace_back(Common::ParamPackage{
{"engine", GetEngineName()},
{"display", std::move(name)},
- {"guid", joystick->GetGUID()},
- {"guid2", joystick2->GetGUID()},
+ {"guid", joystick->GetGUID().RawString()},
+ {"guid2", joystick2->GetGUID().RawString()},
{"port", std::to_string(joystick->GetPort())},
});
}
@@ -531,57 +536,75 @@ Common::Input::VibrationError SDLDriver::SetRumble(
.type = Common::Input::VibrationAmplificationType::Exponential,
};
- if (!joystick->RumblePlay(new_vibration)) {
- return Common::Input::VibrationError::Unknown;
+ if (vibration.type == Common::Input::VibrationAmplificationType::Test) {
+ if (!joystick->RumblePlay(new_vibration)) {
+ return Common::Input::VibrationError::Unknown;
+ }
+ return Common::Input::VibrationError::None;
}
+ vibration_queue.Push(VibrationRequest{
+ .identifier = identifier,
+ .vibration = new_vibration,
+ });
+
return Common::Input::VibrationError::None;
}
-Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, std::string guid,
+void SDLDriver::SendVibrations() {
+ while (!vibration_queue.Empty()) {
+ VibrationRequest request;
+ vibration_queue.Pop(request);
+ const auto joystick = GetSDLJoystickByGUID(request.identifier.guid.RawString(),
+ static_cast<int>(request.identifier.port));
+ joystick->RumblePlay(request.vibration);
+ }
+}
+
+Common::ParamPackage SDLDriver::BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
s32 axis, float value) const {
Common::ParamPackage params{};
params.Set("engine", GetEngineName());
params.Set("port", port);
- params.Set("guid", std::move(guid));
+ params.Set("guid", guid.RawString());
params.Set("axis", axis);
params.Set("threshold", "0.5");
params.Set("invert", value < 0 ? "-" : "+");
return params;
}
-Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, std::string guid,
+Common::ParamPackage SDLDriver::BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
s32 button) const {
Common::ParamPackage params{};
params.Set("engine", GetEngineName());
params.Set("port", port);
- params.Set("guid", std::move(guid));
+ params.Set("guid", guid.RawString());
params.Set("button", button);
return params;
}
-Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
- u8 value) const {
+Common::ParamPackage SDLDriver::BuildHatParamPackageForButton(int port, const Common::UUID& guid,
+ s32 hat, u8 value) const {
Common::ParamPackage params{};
params.Set("engine", GetEngineName());
params.Set("port", port);
- params.Set("guid", std::move(guid));
+ params.Set("guid", guid.RawString());
params.Set("hat", hat);
params.Set("direction", GetHatButtonName(value));
return params;
}
-Common::ParamPackage SDLDriver::BuildMotionParam(int port, std::string guid) const {
+Common::ParamPackage SDLDriver::BuildMotionParam(int port, const Common::UUID& guid) const {
Common::ParamPackage params{};
params.Set("engine", GetEngineName());
params.Set("motion", 0);
params.Set("port", port);
- params.Set("guid", std::move(guid));
+ params.Set("guid", guid.RawString());
return params;
}
Common::ParamPackage SDLDriver::BuildParamPackageForBinding(
- int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const {
+ int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const {
switch (binding.bindType) {
case SDL_CONTROLLER_BINDTYPE_NONE:
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 @@
#include <SDL.h>
#include "common/common_types.h"
+#include "common/threadsafe_queue.h"
#include "input_common/input_engine.h"
union SDL_Event;
@@ -46,6 +47,7 @@ public:
* Check how many identical joysticks (by guid) were connected before the one with sdl_id and so
* tie it to a SDLJoystick with the same guid and that port
*/
+ std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const Common::UUID& guid, int port);
std::shared_ptr<SDLJoystick> GetSDLJoystickByGUID(const std::string& guid, int port);
std::vector<Common::ParamPackage> GetInputDevices() const override;
@@ -64,24 +66,32 @@ public:
const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
private:
+ struct VibrationRequest {
+ PadIdentifier identifier;
+ Common::Input::VibrationStatus vibration;
+ };
+
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
- Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
- float value = 0.1f) const;
- Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid,
+ /// Takes all vibrations from the queue and sends the command to the controller
+ void SendVibrations();
+
+ Common::ParamPackage BuildAnalogParamPackageForButton(int port, const Common::UUID& guid,
+ s32 axis, float value = 0.1f) const;
+ Common::ParamPackage BuildButtonParamPackageForButton(int port, const Common::UUID& guid,
s32 button) const;
- Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat,
+ Common::ParamPackage BuildHatParamPackageForButton(int port, const Common::UUID& guid, s32 hat,
u8 value) const;
- Common::ParamPackage BuildMotionParam(int port, std::string guid) const;
+ Common::ParamPackage BuildMotionParam(int port, const Common::UUID& guid) const;
Common::ParamPackage BuildParamPackageForBinding(
- int port, const std::string& guid, const SDL_GameControllerButtonBind& binding) const;
+ int port, const Common::UUID& guid, const SDL_GameControllerButtonBind& binding) const;
Common::ParamPackage BuildParamPackageForAnalog(PadIdentifier identifier, int axis_x,
int axis_y, float offset_x,
@@ -107,8 +117,11 @@ private:
/// Returns true if the button is on the left joycon
bool IsButtonOnLeftSide(Settings::NativeButton::Values button) const;
+ /// Queue of vibration request to controllers
+ Common::SPSCQueue<VibrationRequest> vibration_queue;
+
/// Map of GUID of a list of corresponding virtual Joysticks
- std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
+ std::unordered_map<Common::UUID, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
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 = {
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier);
+ ReleaseAllTouch();
}
-void TouchScreen::TouchMoved(float x, float y, std::size_t finger) {
- if (finger >= 16) {
+void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
+ const auto index = GetIndexFromFingerId(finger_id);
+ if (!index) {
+ // Touch doesn't exist handle it as a new one
+ TouchPressed(x, y, finger_id);
return;
}
- TouchPressed(x, y, finger);
+ const auto i = index.value();
+ fingers[i].is_active = true;
+ SetButton(identifier, static_cast<int>(i), true);
+ SetAxis(identifier, static_cast<int>(i * 2), x);
+ SetAxis(identifier, static_cast<int>(i * 2 + 1), y);
}
-void TouchScreen::TouchPressed(float x, float y, std::size_t finger) {
- if (finger >= 16) {
+void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
+ if (GetIndexFromFingerId(finger_id)) {
+ // Touch already exist. Just update the data
+ TouchMoved(x, y, finger_id);
return;
}
- SetButton(identifier, static_cast<int>(finger), true);
- SetAxis(identifier, static_cast<int>(finger * 2), x);
- SetAxis(identifier, static_cast<int>(finger * 2 + 1), y);
+ const auto index = GetNextFreeIndex();
+ if (!index) {
+ // No free entries. Ignore input
+ return;
+ }
+ const auto i = index.value();
+ fingers[i].is_enabled = true;
+ fingers[i].finger_id = finger_id;
+ TouchMoved(x, y, finger_id);
}
-void TouchScreen::TouchReleased(std::size_t finger) {
- if (finger >= 16) {
+void TouchScreen::TouchReleased(std::size_t finger_id) {
+ const auto index = GetIndexFromFingerId(finger_id);
+ if (!index) {
return;
}
- SetButton(identifier, static_cast<int>(finger), false);
- SetAxis(identifier, static_cast<int>(finger * 2), 0.0f);
- SetAxis(identifier, static_cast<int>(finger * 2 + 1), 0.0f);
+ const auto i = index.value();
+ fingers[i].is_enabled = false;
+ SetButton(identifier, static_cast<int>(i), false);
+ SetAxis(identifier, static_cast<int>(i * 2), 0.0f);
+ SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f);
+}
+
+std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {
+ for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
+ const auto& finger = fingers[index];
+ if (!finger.is_enabled) {
+ continue;
+ }
+ if (finger.finger_id == finger_id) {
+ return index;
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<std::size_t> TouchScreen::GetNextFreeIndex() const {
+ for (std::size_t index = 0; index < MAX_FINGER_COUNT; ++index) {
+ if (!fingers[index].is_enabled) {
+ return index;
+ }
+ }
+ return std::nullopt;
+}
+
+void TouchScreen::ClearActiveFlag() {
+ for (auto& finger : fingers) {
+ finger.is_active = false;
+ }
+}
+
+void TouchScreen::ReleaseInactiveTouch() {
+ for (const auto& finger : fingers) {
+ if (!finger.is_active) {
+ TouchReleased(finger.finger_id);
+ }
+ }
}
void TouchScreen::ReleaseAllTouch() {
- for (int index = 0; index < 16; ++index) {
- SetButton(identifier, index, false);
- SetAxis(identifier, index * 2, 0.0f);
- SetAxis(identifier, index * 2 + 1, 0.0f);
+ for (const auto& finger : fingers) {
+ if (finger.is_enabled) {
+ TouchReleased(finger.finger_id);
+ }
}
}
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 @@
#pragma once
+#include <optional>
+
#include "input_common/input_engine.h"
namespace InputCommon {
/**
- * A button device factory representing a keyboard. It receives keyboard events and forward them
- * to all button devices it created.
+ * A touch device factory representing a touch screen. It receives touch events and forward them
+ * to all touch devices it created.
*/
class TouchScreen final : public InputEngine {
public:
explicit TouchScreen(std::string input_engine_);
/**
- * Signals that mouse has moved.
- * @param x the x-coordinate of the cursor
- * @param y the y-coordinate of the cursor
- * @param center_x the x-coordinate of the middle of the screen
- * @param center_y the y-coordinate of the middle of the screen
+ * Signals that touch has moved and marks this touch point as active
+ * @param x new horizontal position
+ * @param y new vertical position
+ * @param finger_id of the touch point to be updated
*/
- void TouchMoved(float x, float y, std::size_t finger);
+ void TouchMoved(float x, float y, std::size_t finger_id);
/**
- * Sets the status of all buttons bound with the key to pressed
- * @param key_code the code of the key to press
+ * Signals and creates a new touch point with this finger id
+ * @param x starting horizontal position
+ * @param y starting vertical position
+ * @param finger_id to be assigned to the new touch point
*/
- void TouchPressed(float x, float y, std::size_t finger);
+ void TouchPressed(float x, float y, std::size_t finger_id);
/**
- * Sets the status of all buttons bound with the key to released
- * @param key_code the code of the key to release
+ * Signals and resets the touch point related to the this finger id
+ * @param finger_id to be released
*/
- void TouchReleased(std::size_t finger);
+ void TouchReleased(std::size_t finger_id);
+
+ /// Resets the active flag for each touch point
+ void ClearActiveFlag();
+
+ /// Releases all touch that haven't been marked as active
+ void ReleaseInactiveTouch();
/// Resets all inputs to their initial value
void ReleaseAllTouch();
+
+private:
+ static constexpr std::size_t MAX_FINGER_COUNT = 16;
+
+ struct TouchStatus {
+ std::size_t finger_id{};
+ bool is_enabled{};
+ bool is_active{};
+ };
+
+ std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
+
+ std::optional<std::size_t> GetNextFreeIndex() const;
+
+ std::array<TouchStatus, MAX_FINGER_COUNT> fingers{};
};
} // 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()
-Werror
-Werror=conversion
-Werror=ignored-qualifiers
- -Werror=implicit-fallthrough
- -Werror=shadow
- -Werror=sign-compare
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
-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> {
}
template <typename FormatContext>
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
- return format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
+ return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
}
};
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:
[[nodiscard]] Stack Remove(Token token) const;
private:
- boost::container::small_vector<StackEntry, 3> entries;
+ std::vector<StackEntry> entries;
};
struct 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> {
}
template <typename FormatContext>
auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
- return format_to(ctx.out(), "{}", NameOf(opcode));
+ return fmt::format_to(ctx.out(), "{}", NameOf(opcode));
}
};
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:
Environment& env;
IR::AbstractSyntaxList& syntax_list;
bool uses_demote_to_helper{};
-
-// TODO: C++20 Remove this when all compilers support constexpr std::vector
-#if __cpp_lib_constexpr_vector >= 201907
- static constexpr Flow::Block dummy_flow_block;
-#else
const Flow::Block dummy_flow_block;
-#endif
};
} // Anonymous namespace
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)
target_compile_options(video_core PRIVATE
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
- /we4456 # Declaration of 'identifier' hides previous local declaration
- /we4457 # Declaration of 'identifier' hides function parameter
- /we4458 # Declaration of 'identifier' hides class member
- /we4459 # Declaration of 'identifier' hides global declaration
)
else()
target_compile_options(video_core PRIVATE
@@ -269,7 +265,6 @@ else()
-Wno-error=sign-conversion
-Werror=pessimizing-move
-Werror=redundant-move
- -Werror=shadow
-Werror=type-limits
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
@@ -277,3 +272,7 @@ else()
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
)
endif()
+
+if (ARCHITECTURE_x86_64)
+ target_link_libraries(video_core PRIVATE dynarmic)
+endif()
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() {
vp9_hidden_frame = vp9_decoder->WasFrameHidden();
return vp9_decoder->GetFrameBytes();
default:
- UNREACHABLE();
+ ASSERT(false);
return std::vector<u8>{};
}
}();
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{
.high_precision{128, 128},
};
-constexpr std::array<s32, 256> norm_lut{
+constexpr std::array<u8, 256> norm_lut{
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,
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,
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{
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,
};
-constexpr std::array<s32, 254> map_lut{
+constexpr std::array<u8, 254> map_lut{
20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
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{
std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
}
- return map_lut[index];
+ return static_cast<s32>(map_lut[index]);
}
} // Anonymous namespace
@@ -819,7 +819,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
local_range = range - split;
}
- s32 shift = norm_lut[local_range];
+ s32 shift = static_cast<s32>(norm_lut[local_range]);
local_range <<= shift;
count += shift;
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) {
break;
}
default:
- UNREACHABLE();
+ ASSERT(false);
break;
}
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
case MAXWELL3D_REG_INDEX(shadow_ram_control):
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
return;
+ case MAXWELL3D_REG_INDEX(macros.upload_address):
+ return macro_engine->ClearCode(regs.macros.upload_address);
case MAXWELL3D_REG_INDEX(macros.data):
return macro_engine->AddCode(regs.macros.upload_address, argument);
case MAXWELL3D_REG_INDEX(macros.bind):
@@ -593,8 +595,8 @@ void Maxwell3D::DrawArrays() {
std::optional<u64> Maxwell3D::GetQueryResult() {
switch (regs.query.query_get.select) {
- case Regs::QuerySelect::Zero:
- return 0;
+ case Regs::QuerySelect::Payload:
+ return regs.query.query_sequence;
case Regs::QuerySelect::SamplesPassed:
// Deferred.
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:
};
enum class QuerySelect : u32 {
- Zero = 0,
+ Payload = 0,
TimeElapsed = 2,
TransformFeedbackPrimitivesGenerated = 11,
PrimitivesGenerated = 18,
@@ -202,7 +202,7 @@ public:
case Size::Size_11_11_10:
return 3;
default:
- UNREACHABLE();
+ ASSERT(false);
return 1;
}
}
@@ -238,7 +238,7 @@ public:
case Size::Size_11_11_10:
return 4;
default:
- UNREACHABLE();
+ ASSERT(false);
return 1;
}
}
@@ -274,7 +274,7 @@ public:
case Size::Size_11_11_10:
return "11_11_10";
default:
- UNREACHABLE();
+ ASSERT(false);
return {};
}
}
@@ -296,7 +296,7 @@ public:
case Type::Float:
return "FLOAT";
}
- UNREACHABLE();
+ ASSERT(false);
return {};
}
@@ -336,7 +336,7 @@ public:
case 3:
return {x3, y3};
default:
- UNREACHABLE();
+ ASSERT(false);
return {0, 0};
}
}
@@ -1193,7 +1193,7 @@ public:
case IndexFormat::UnsignedInt:
return 4;
}
- UNREACHABLE();
+ ASSERT(false);
return 1;
}
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() {
if (!is_src_pitch && !is_dst_pitch) {
// If both the source and the destination are in block layout, assert.
- UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
+ UNIMPLEMENTED_MSG("Tiled->Tiled DMA transfers are not yet implemented");
return;
}
@@ -134,7 +134,8 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
// Deswizzle the input and copy it over.
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
- const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
+ const u32 bytes_per_pixel =
+ regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
const Parameters& src_params = regs.src_params;
const u32 width = src_params.width;
const u32 height = src_params.height;
@@ -166,7 +167,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
UNIMPLEMENTED_IF(regs.launch_dma.remap_enable != 0);
const auto& dst_params = regs.dst_params;
- const u32 bytes_per_pixel = regs.pitch_in / regs.line_length_in;
+ const u32 bytes_per_pixel =
+ regs.launch_dma.remap_enable ? regs.pitch_in / regs.line_length_in : 1;
const u32 width = dst_params.width;
const u32 height = dst_params.height;
const u32 depth = dst_params.depth;
@@ -210,7 +212,8 @@ void MaxwellDMA::CopyPitchToBlockLinear() {
}
void MaxwellDMA::FastCopyBlockLinearToPitch() {
- const u32 bytes_per_pixel = regs.pitch_out / regs.line_length_in;
+ const u32 bytes_per_pixel =
+ regs.launch_dma.remap_enable ? regs.pitch_out / regs.line_length_in : 1;
const size_t src_size = GOB_SIZE;
const size_t dst_size = static_cast<size_t>(regs.pitch_out) * regs.line_count;
u32 pos_x = regs.src_params.origin.x;
@@ -257,7 +260,7 @@ void MaxwellDMA::ReleaseSemaphore() {
memory_manager.Write<u64>(address + 8, system.GPU().GetTicks());
break;
default:
- UNREACHABLE_MSG("Unknown semaphore type: {}", static_cast<u32>(type.Value()));
+ ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
}
}
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,
VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
while (!stop_token.stop_requested()) {
- CommandDataContainer next = state.queue.PopWait(stop_token);
+ CommandDataContainer next;
+ state.queue.Pop(next, stop_token);
if (stop_token.stop_requested()) {
break;
}
@@ -49,7 +50,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
} else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
} else {
- UNREACHABLE();
+ ASSERT(false);
}
state.signaled_fence.store(next.fence);
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 @@
#include <thread>
#include <variant>
-#include "common/threadsafe_queue.h"
+#include "common/bounded_threadsafe_queue.h"
#include "video_core/framebuffer_config.h"
namespace Tegra {
@@ -96,9 +96,9 @@ struct CommandDataContainer {
/// Struct used to synchronize the GPU thread
struct SynchState final {
- using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
+ using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
std::mutex write_lock;
- CommandQueue queue;
+ CommandQueue queue{512}; // size must be 2^n
u64 last_fence{};
std::atomic<u64> signaled_fence{};
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 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
+#include <fstream>
#include <optional>
+#include <span>
#include <boost/container_hash/hash.hpp>
#include "common/assert.h"
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
#include "common/settings.h"
#include "video_core/macro/macro.h"
#include "video_core/macro/macro_hle.h"
@@ -15,6 +19,23 @@
namespace Tegra {
+static void Dump(u64 hash, std::span<const u32> code) {
+ const auto base_dir{Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir)};
+ const auto macro_dir{base_dir / "macros"};
+ if (!Common::FS::CreateDir(base_dir) || !Common::FS::CreateDir(macro_dir)) {
+ LOG_ERROR(Common_Filesystem, "Failed to create macro dump directories");
+ return;
+ }
+ const auto name{macro_dir / fmt::format("{:016x}.macro", hash)};
+ std::fstream macro_file(name, std::ios::out | std::ios::binary);
+ if (!macro_file) {
+ LOG_ERROR(Common_Filesystem, "Unable to open or create file at {}",
+ Common::FS::PathToUTF8String(name));
+ return;
+ }
+ macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
+}
+
MacroEngine::MacroEngine(Engines::Maxwell3D& maxwell3d)
: hle_macros{std::make_unique<Tegra::HLEMacro>(maxwell3d)} {}
@@ -24,6 +45,11 @@ void MacroEngine::AddCode(u32 method, u32 data) {
uploaded_macro_code[method].push_back(data);
}
+void MacroEngine::ClearCode(u32 method) {
+ macro_cache.erase(method);
+ uploaded_macro_code.erase(method);
+}
+
void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
auto compiled_macro = macro_cache.find(method);
if (compiled_macro != macro_cache.end()) {
@@ -45,7 +71,7 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
}
}
if (!mid_method.has_value()) {
- UNREACHABLE_MSG("Macro 0x{0:x} was not uploaded", method);
+ ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
return;
}
}
@@ -54,6 +80,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
if (!mid_method.has_value()) {
cache_info.lle_program = Compile(macro_code->second);
cache_info.hash = boost::hash_value(macro_code->second);
+ if (Settings::values.dump_macros) {
+ Dump(cache_info.hash, macro_code->second);
+ }
} else {
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
const auto rebased_method = method - mid_method.value();
@@ -63,6 +92,9 @@ void MacroEngine::Execute(u32 method, const std::vector<u32>& parameters) {
code.size() * sizeof(u32));
cache_info.hash = boost::hash_value(code);
cache_info.lle_program = Compile(code);
+ if (Settings::values.dump_macros) {
+ Dump(cache_info.hash, code);
+ }
}
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:
// Store the uploaded macro code to compile them when they're called.
void AddCode(u32 method, u32 data);
+ // Clear the code associated with a method.
+ void ClearCode(u32 method);
+
// Compiles the macro if its not in the cache, and executes the compiled macro
void Execute(u32 method, const std::vector<u32>& parameters);
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,
return value != 0;
}
UNREACHABLE();
- return true;
}
Macro::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
namespace Tegra {
namespace {
constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx;
-constexpr Xbyak::Reg32 RESULT = Xbyak::util::ebp;
+constexpr Xbyak::Reg32 RESULT = Xbyak::util::r10d;
+constexpr Xbyak::Reg64 MAX_PARAMETER = Xbyak::util::r11;
constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
@@ -31,6 +32,7 @@ constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
constexpr std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
STATE,
RESULT,
+ MAX_PARAMETER,
PARAMETERS,
METHOD_ADDRESS,
BRANCH_HOLDER,
@@ -80,7 +82,7 @@ private:
u32 carry_flag{};
};
static_assert(offsetof(JITState, maxwell3d) == 0, "Maxwell3D is not at 0x0");
- using ProgramType = void (*)(JITState*, const u32*);
+ using ProgramType = void (*)(JITState*, const u32*, const u32*);
struct OptimizerState {
bool can_skip_carry{};
@@ -112,7 +114,7 @@ void MacroJITx64Impl::Execute(const std::vector<u32>& parameters, u32 method) {
JITState state{};
state.maxwell3d = &maxwell3d;
state.registers = {};
- program(&state, parameters.data());
+ program(&state, parameters.data(), parameters.data() + parameters.size());
}
void MacroJITx64Impl::Compile_ALU(Macro::Opcode opcode) {
@@ -409,7 +411,7 @@ void MacroJITx64Impl::Compile_Branch(Macro::Opcode opcode) {
Xbyak::Label end;
auto value = Compile_GetRegister(opcode.src_a, eax);
- test(value, value);
+ cmp(value, 0); // test(value, value);
if (optimizer.has_delayed_pc) {
switch (opcode.branch_condition) {
case Macro::BranchCondition::Zero:
@@ -488,6 +490,7 @@ void MacroJITx64Impl::Compile() {
// JIT state
mov(STATE, Common::X64::ABI_PARAM1);
mov(PARAMETERS, Common::X64::ABI_PARAM2);
+ mov(MAX_PARAMETER, Common::X64::ABI_PARAM3);
xor_(RESULT, RESULT);
xor_(METHOD_ADDRESS, METHOD_ADDRESS);
xor_(BRANCH_HOLDER, BRANCH_HOLDER);
@@ -598,7 +601,22 @@ bool MacroJITx64Impl::Compile_NextInstruction() {
return true;
}
+static void WarnInvalidParameter(uintptr_t parameter, uintptr_t max_parameter) {
+ LOG_CRITICAL(HW_GPU,
+ "Macro JIT: invalid parameter access 0x{:x} (0x{:x} is the last parameter)",
+ parameter, max_parameter - sizeof(u32));
+}
+
Xbyak::Reg32 MacroJITx64Impl::Compile_FetchParameter() {
+ Xbyak::Label parameter_ok{};
+ cmp(PARAMETERS, MAX_PARAMETER);
+ jb(parameter_ok, T_NEAR);
+ Common::X64::ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
+ mov(Common::X64::ABI_PARAM1, PARAMETERS);
+ mov(Common::X64::ABI_PARAM2, MAX_PARAMETER);
+ Common::X64::CallFarFunction(*this, &WarnInvalidParameter);
+ Common::X64::ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0);
+ L(parameter_ok);
mov(eax, dword[PARAMETERS]);
add(PARAMETERS, sizeof(u32));
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) {
ASSERT(it->first == gpu_addr);
map_ranges.erase(it);
} else {
- UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr);
+ ASSERT_MSG(false, "Unmapping non-existent GPU address=0x{:x}", gpu_addr);
}
const auto submapped_ranges = GetSubmappedRange(gpu_addr, size);
@@ -206,7 +206,7 @@ T MemoryManager::Read(GPUVAddr addr) const {
return value;
}
- UNREACHABLE();
+ ASSERT(false);
return {};
}
@@ -219,7 +219,7 @@ void MemoryManager::Write(GPUVAddr addr, T data) {
return;
}
- UNREACHABLE();
+ ASSERT(false);
}
template 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) {
case 4:
return GL_FRAGMENT_SHADER;
}
- UNREACHABLE_MSG("{}", stage_index);
+ ASSERT_MSG(false, "{}", stage_index);
return GL_NONE;
}
@@ -65,7 +65,7 @@ GLenum AssemblyStage(size_t stage_index) {
case 4:
return GL_FRAGMENT_PROGRAM_NV;
}
- UNREACHABLE_MSG("{}", stage_index);
+ ASSERT_MSG(false, "{}", stage_index);
return GL_NONE;
}
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() {
flags[Dirty::FrontFace] = false;
GLenum mode = MaxwellToGL::FrontFace(regs.front_face);
- bool flip_faces = false;
- if (regs.screen_y_control.triangle_rast_flip != 0 &&
- regs.viewport_transform[0].scale_y < 0.0f) {
+ bool flip_faces = true;
+ if (regs.screen_y_control.triangle_rast_flip != 0) {
flip_faces = !flip_faces;
}
- if (regs.viewport_transform[0].scale_z < 0.0f) {
+ if (regs.viewport_transform[0].scale_y < 0.0f) {
flip_faces = !flip_faces;
}
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,
case Maxwell::TessellationPrimitive::Quads:
return Shader::TessPrimitive::Quads;
}
- UNREACHABLE();
+ ASSERT(false);
return Shader::TessPrimitive::Triangles;
}();
info.tess_spacing = [&] {
@@ -97,7 +97,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
case Maxwell::TessellationSpacing::FractionalEven:
return Shader::TessSpacing::FractionalEven;
}
- UNREACHABLE();
+ ASSERT(false);
return Shader::TessSpacing::Equal;
}();
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) {
case ImageType::Buffer:
return GL_TEXTURE_BUFFER;
}
- UNREACHABLE_MSG("Invalid image type={}", info.type);
+ ASSERT_MSG(false, "Invalid image type={}", info.type);
return GL_NONE;
}
@@ -107,7 +107,7 @@ GLenum ImageTarget(Shader::TextureType type, int num_samples = 1) {
case Shader::TextureType::Buffer:
return GL_TEXTURE_BUFFER;
}
- UNREACHABLE_MSG("Invalid image view type={}", type);
+ ASSERT_MSG(false, "Invalid image view type={}", type);
return GL_NONE;
}
@@ -119,7 +119,7 @@ GLenum TextureMode(PixelFormat format, bool is_first) {
case PixelFormat::S8_UINT_D24_UNORM:
return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
default:
- UNREACHABLE();
+ ASSERT(false);
return GL_DEPTH_COMPONENT;
}
}
@@ -140,7 +140,7 @@ GLint Swizzle(SwizzleSource source) {
case SwizzleSource::OneFloat:
return GL_ONE;
}
- UNREACHABLE_MSG("Invalid swizzle source={}", source);
+ ASSERT_MSG(false, "Invalid swizzle source={}", source);
return GL_NONE;
}
@@ -197,7 +197,7 @@ GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
case SwizzleSource::OneFloat:
return GL_ONE;
}
- UNREACHABLE_MSG("Invalid swizzle source={}", source);
+ ASSERT_MSG(false, "Invalid swizzle source={}", source);
return GL_NONE;
}
@@ -381,10 +381,10 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
glTextureStorage3D(handle, gl_num_levels, gl_internal_format, width, height, depth);
break;
case GL_TEXTURE_BUFFER:
- UNREACHABLE();
+ ASSERT(false);
break;
default:
- UNREACHABLE_MSG("Invalid target=0x{:x}", target);
+ ASSERT_MSG(false, "Invalid target=0x{:x}", target);
break;
}
return texture;
@@ -420,7 +420,7 @@ OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_form
case Shader::ImageFormat::R32G32B32A32_UINT:
return GL_RGBA32UI;
}
- UNREACHABLE_MSG("Invalid image format={}", format);
+ ASSERT_MSG(false, "Invalid image format={}", format);
return GL_R32UI;
}
@@ -579,7 +579,7 @@ void TextureCacheRuntime::EmulateCopyImage(Image& dst, Image& src,
} else if (IsPixelFormatBGR(dst.info.format) || IsPixelFormatBGR(src.info.format)) {
format_conversion_pass.ConvertImage(dst, src, copies);
} else {
- UNREACHABLE();
+ ASSERT(false);
}
}
@@ -620,7 +620,7 @@ void TextureCacheRuntime::AccelerateImageUpload(Image& image, const ImageBufferM
case ImageType::Linear:
return util_shaders.PitchUpload(image, map, swizzles);
default:
- UNREACHABLE();
+ ASSERT(false);
break;
}
}
@@ -639,7 +639,7 @@ FormatProperties TextureCacheRuntime::FormatInfo(ImageType type, GLenum internal
case ImageType::e3D:
return format_properties[2].at(internal_format);
default:
- UNREACHABLE();
+ ASSERT(false);
return FormatProperties{};
}
}
@@ -888,7 +888,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
}
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
}
@@ -924,7 +924,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
depth = copy.image_extent.depth;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
// Compressed formats don't have a pixel format or type
const bool is_compressed = gl_format == GL_NONE;
@@ -950,7 +950,7 @@ void Image::Scale(bool up_scale) {
case SurfaceType::DepthStencil:
return GL_DEPTH_STENCIL_ATTACHMENT;
default:
- UNREACHABLE();
+ ASSERT(false);
return GL_COLOR_ATTACHMENT0;
}
}();
@@ -965,7 +965,7 @@ void Image::Scale(bool up_scale) {
case SurfaceType::DepthStencil:
return GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
default:
- UNREACHABLE();
+ ASSERT(false);
return GL_COLOR_BUFFER_BIT;
}
}();
@@ -980,7 +980,7 @@ void Image::Scale(bool up_scale) {
case SurfaceType::DepthStencil:
return 3;
default:
- UNREACHABLE();
+ ASSERT(false);
return 0;
}
}();
@@ -1045,7 +1045,7 @@ bool Image::ScaleUp(bool ignore) {
return false;
}
if (info.type == ImageType::Linear) {
- UNREACHABLE();
+ ASSERT(false);
return false;
}
flags |= ImageFlagBits::Rescaled;
@@ -1139,7 +1139,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
UNIMPLEMENTED();
break;
case ImageViewType::Buffer:
- UNREACHABLE();
+ ASSERT(false);
break;
}
switch (info.type) {
@@ -1319,7 +1319,7 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
buffer_bits |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
buffer_bits |= GL_DEPTH_BUFFER_BIT;
break;
}
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) {
case Maxwell::IndexFormat::UnsignedInt:
return GL_UNSIGNED_INT;
}
- UNREACHABLE_MSG("Invalid index_format={}", index_format);
+ ASSERT_MSG(false, "Invalid index_format={}", index_format);
return {};
}
@@ -243,7 +243,7 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
case Maxwell::PrimitiveTopology::Patches:
return GL_PATCHES;
}
- UNREACHABLE_MSG("Invalid topology={}", topology);
+ ASSERT_MSG(false, "Invalid topology={}", topology);
return GL_POINTS;
}
@@ -271,8 +271,8 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
}
break;
}
- UNREACHABLE_MSG("Invalid texture filter mode={} and mipmap filter mode={}", filter_mode,
- mipmap_filter_mode);
+ ASSERT_MSG(false, "Invalid texture filter mode={} and mipmap filter mode={}", filter_mode,
+ mipmap_filter_mode);
return GL_NEAREST;
}
@@ -550,7 +550,7 @@ inline GLenum PolygonMode(Maxwell::PolygonMode polygon_mode) {
case Maxwell::PolygonMode::Fill:
return GL_FILL;
}
- UNREACHABLE_MSG("Invalid polygon mode={}", polygon_mode);
+ ASSERT_MSG(false, "Invalid polygon mode={}", polygon_mode);
return GL_FILL;
}
@@ -563,7 +563,7 @@ inline GLenum ReductionFilter(Tegra::Texture::SamplerReduction filter) {
case Tegra::Texture::SamplerReduction::Max:
return GL_MAX;
}
- UNREACHABLE_MSG("Invalid reduction filter={}", static_cast<int>(filter));
+ ASSERT_MSG(false, "Invalid reduction filter={}", static_cast<int>(filter));
return GL_WEIGHTED_AVERAGE_ARB;
}
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) {
case GL_DEBUG_SOURCE_OTHER:
return "OTHER";
default:
- UNREACHABLE();
+ ASSERT(false);
return "Unknown source";
}
}
@@ -101,7 +101,7 @@ const char* GetType(GLenum type) {
case GL_DEBUG_TYPE_MARKER:
return "MARKER";
default:
- UNREACHABLE();
+ ASSERT(false);
return "Unknown type";
}
}
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) {
case 16:
return GL_RGBA32UI;
}
- UNREACHABLE();
+ ASSERT(false);
return GL_R8UI;
}
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) {
case Tegra::Texture::TextureFilter::Linear:
return VK_FILTER_LINEAR;
}
- UNREACHABLE_MSG("Invalid sampler filter={}", filter);
+ ASSERT_MSG(false, "Invalid sampler filter={}", filter);
return {};
}
@@ -42,7 +42,7 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
case Tegra::Texture::TextureMipmapFilter::Linear:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
- UNREACHABLE_MSG("Invalid sampler mipmap mode={}", mipmap_filter);
+ ASSERT_MSG(false, "Invalid sampler mipmap mode={}", mipmap_filter);
return {};
}
@@ -70,7 +70,7 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
case Tegra::Texture::TextureFilter::Linear:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
}
- UNREACHABLE();
+ ASSERT(false);
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
@@ -744,7 +744,7 @@ VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle)
case Maxwell::ViewportSwizzle::NegativeW:
return VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV;
}
- UNREACHABLE_MSG("Invalid swizzle={}", swizzle);
+ ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
return {};
}
@@ -757,7 +757,7 @@ VkSamplerReductionMode SamplerReduction(Tegra::Texture::SamplerReduction reducti
case Tegra::Texture::SamplerReduction::Max:
return VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
}
- UNREACHABLE_MSG("Invalid sampler mode={}", static_cast<int>(reduction));
+ ASSERT_MSG(false, "Invalid sampler mode={}", static_cast<int>(reduction));
return VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
}
@@ -780,7 +780,7 @@ VkSampleCountFlagBits MsaaMode(Tegra::Texture::MsaaMode msaa_mode) {
case Tegra::Texture::MsaaMode::Msaa4x4:
return VK_SAMPLE_COUNT_16_BIT;
default:
- UNREACHABLE_MSG("Invalid msaa_mode={}", static_cast<int>(msaa_mode));
+ ASSERT_MSG(false, "Invalid msaa_mode={}", static_cast<int>(msaa_mode));
return VK_SAMPLE_COUNT_1_BIT;
}
}
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) {
case VK_INDEX_TYPE_UINT32:
return 4;
default:
- UNREACHABLE_MSG("Invalid index type={}", index_type);
+ ASSERT_MSG(false, "Invalid index type={}", index_type);
return 1;
}
}
@@ -366,7 +366,7 @@ void BufferCacheRuntime::ReserveQuadArrayLUT(u32 num_indices, bool wait_for_idle
std::memcpy(staging_data, MakeQuadIndices<u32>(quad, first).data(), quad_size);
break;
default:
- UNREACHABLE();
+ ASSERT(false);
break;
}
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(
case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt:
return 2;
}
- UNREACHABLE();
+ ASSERT(false);
return 2;
}();
const u32 input_size = num_vertices << index_shift;
@@ -328,31 +328,32 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
const VkImageAspectFlags aspect_mask = image.AspectMask();
const VkImage vk_image = image.Handle();
const bool is_initialized = image.ExchangeInitialization();
- scheduler.Record(
- [vk_pipeline, vk_image, aspect_mask, is_initialized](vk::CommandBuffer cmdbuf) {
- const VkImageMemoryBarrier image_barrier{
- .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
- .pNext = nullptr,
- .srcAccessMask = is_initialized ? VK_ACCESS_SHADER_WRITE_BIT : VkAccessFlags{},
- .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
- .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
- .newLayout = VK_IMAGE_LAYOUT_GENERAL,
- .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
- .image = vk_image,
- .subresourceRange{
- .aspectMask = aspect_mask,
- .baseMipLevel = 0,
- .levelCount = VK_REMAINING_MIP_LEVELS,
- .baseArrayLayer = 0,
- .layerCount = VK_REMAINING_ARRAY_LAYERS,
- },
- };
- cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT
- : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
- VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
- });
+ scheduler.Record([vk_pipeline, vk_image, aspect_mask,
+ is_initialized](vk::CommandBuffer cmdbuf) {
+ const VkImageMemoryBarrier image_barrier{
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .pNext = nullptr,
+ .srcAccessMask = static_cast<VkAccessFlags>(is_initialized ? VK_ACCESS_SHADER_WRITE_BIT
+ : VK_ACCESS_NONE),
+ .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
+ .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_GENERAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = vk_image,
+ .subresourceRange{
+ .aspectMask = aspect_mask,
+ .baseMipLevel = 0,
+ .levelCount = VK_REMAINING_MIP_LEVELS,
+ .baseArrayLayer = 0,
+ .layerCount = VK_REMAINING_ARRAY_LAYERS,
+ },
+ };
+ cmdbuf.PipelineBarrier(is_initialized ? VK_PIPELINE_STAGE_ALL_COMMANDS_BIT
+ : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
+ });
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
const size_t input_offset = swizzle.buffer_offset + map.offset;
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
case Maxwell::TessellationPrimitive::Quads:
return Shader::TessPrimitive::Quads;
}
- UNREACHABLE();
+ ASSERT(false);
return Shader::TessPrimitive::Triangles;
}();
info.tess_spacing = [&] {
@@ -187,7 +187,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
case Maxwell::TessellationSpacing::FractionalEven:
return Shader::TessSpacing::FractionalEven;
}
- UNREACHABLE();
+ ASSERT(false);
return Shader::TessSpacing::Equal;
}();
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)
});
} else {
// Front face defines both faces
- scheduler.Record([ref = regs.stencil_back_func_ref, write_mask = regs.stencil_back_mask,
- test_mask = regs.stencil_back_func_mask](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([ref = regs.stencil_front_func_ref, write_mask = regs.stencil_front_mask,
+ test_mask = regs.stencil_front_func_mask](vk::CommandBuffer cmdbuf) {
cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_AND_BACK, ref);
cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_AND_BACK, write_mask);
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
case MemoryUsage::Download:
return download_cache;
default:
- UNREACHABLE_MSG("Invalid memory usage={}", usage);
+ ASSERT_MSG(false, "Invalid memory usage={}", usage);
return upload_cache;
}
}
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) {
case ImageType::Buffer:
break;
}
- UNREACHABLE_MSG("Invalid image type={}", type);
+ ASSERT_MSG(false, "Invalid image type={}", type);
return {};
}
@@ -87,7 +87,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case 16:
return VK_SAMPLE_COUNT_16_BIT;
default:
- UNREACHABLE_MSG("Invalid number of samples={}", num_samples);
+ ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
return VK_SAMPLE_COUNT_1_BIT;
}
}
@@ -107,7 +107,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
break;
default:
- UNREACHABLE_MSG("Invalid surface type");
+ ASSERT_MSG(false, "Invalid surface type");
}
}
if (info.storage) {
@@ -179,7 +179,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case VideoCore::Surface::SurfaceType::DepthStencil:
return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
default:
- UNREACHABLE_MSG("Invalid surface type");
+ ASSERT_MSG(false, "Invalid surface type");
return VkImageAspectFlags{};
}
}
@@ -221,7 +221,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case SwizzleSource::OneInt:
return VK_COMPONENT_SWIZZLE_ONE;
}
- UNREACHABLE_MSG("Invalid swizzle={}", swizzle);
+ ASSERT_MSG(false, "Invalid swizzle={}", swizzle);
return VK_COMPONENT_SWIZZLE_ZERO;
}
@@ -242,10 +242,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
case Shader::TextureType::ColorArrayCube:
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
case Shader::TextureType::Buffer:
- UNREACHABLE_MSG("Texture buffers can't be image views");
+ ASSERT_MSG(false, "Texture buffers can't be image views");
return VK_IMAGE_VIEW_TYPE_1D;
}
- UNREACHABLE_MSG("Invalid image view type={}", type);
+ ASSERT_MSG(false, "Invalid image view type={}", type);
return VK_IMAGE_VIEW_TYPE_2D;
}
@@ -269,10 +269,10 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
UNIMPLEMENTED_MSG("Rect image view");
return VK_IMAGE_VIEW_TYPE_2D;
case VideoCommon::ImageViewType::Buffer:
- UNREACHABLE_MSG("Texture buffers can't be image views");
+ ASSERT_MSG(false, "Texture buffers can't be image views");
return VK_IMAGE_VIEW_TYPE_1D;
}
- UNREACHABLE_MSG("Invalid image view type={}", type);
+ ASSERT_MSG(false, "Invalid image view type={}", type);
return VK_IMAGE_VIEW_TYPE_2D;
}
@@ -644,7 +644,7 @@ struct RangedBarrierRange {
case Shader::ImageFormat::R32G32B32A32_UINT:
return VK_FORMAT_R32G32B32A32_UINT;
}
- UNREACHABLE_MSG("Invalid image format={}", format);
+ ASSERT_MSG(false, "Invalid image format={}", format);
return VK_FORMAT_R32_UINT;
}
@@ -1596,7 +1596,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
UNIMPLEMENTED();
break;
case VideoCommon::ImageViewType::Buffer:
- UNREACHABLE();
+ ASSERT(false);
break;
}
}
@@ -1822,7 +1822,7 @@ void TextureCacheRuntime::AccelerateImageUpload(
if (IsPixelFormatASTC(image.info.format)) {
return astc_decoder_pass.Assemble(image, map, swizzles);
}
- UNREACHABLE();
+ ASSERT(false);
}
} // 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_,
stage_index = 4;
break;
default:
- UNREACHABLE_MSG("Invalid program={}", program);
+ ASSERT_MSG(false, "Invalid program={}", program);
break;
}
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
return SurfaceTarget::Texture2DArray;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", texture_type);
- UNREACHABLE();
+ ASSERT(false);
return SurfaceTarget::Texture2D;
}
}
@@ -48,7 +48,7 @@ bool SurfaceTargetIsLayered(SurfaceTarget target) {
return true;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
- UNREACHABLE();
+ ASSERT(false);
return false;
}
}
@@ -67,7 +67,7 @@ bool SurfaceTargetIsArray(SurfaceTarget target) {
return true;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
- UNREACHABLE();
+ ASSERT(false);
return false;
}
}
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 {
TextureCubeArray,
};
-constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
+constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
1, // A8B8G8R8_UNORM
1, // A8B8G8R8_SNORM
1, // A8B8G8R8_SINT
@@ -249,7 +249,7 @@ constexpr u32 DefaultBlockWidth(PixelFormat format) {
return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
}
-constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
+constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
1, // A8B8G8R8_UNORM
1, // A8B8G8R8_SNORM
1, // A8B8G8R8_SINT
@@ -351,7 +351,7 @@ constexpr u32 DefaultBlockHeight(PixelFormat format) {
return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
}
-constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
+constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
32, // A8B8G8R8_UNORM
32, // A8B8G8R8_SNORM
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 {
resources.layers = 1;
break;
default:
- UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
+ ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
break;
}
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
range.extent.layers = config.Depth() * 6;
break;
default:
- UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
+ ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
break;
}
}
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 {
case 16:
return {2, 2};
}
- UNREACHABLE_MSG("Invalid number of samples={}", num_samples);
+ ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
return {1, 1};
}
@@ -47,7 +47,7 @@ namespace VideoCommon {
case MsaaMode::Msaa4x4:
return 16;
}
- UNREACHABLE_MSG("Invalid MSAA mode={}", static_cast<int>(msaa_mode));
+ ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
return 1;
}
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) {
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
const auto page_it = selected_page_table.find(page);
if (page_it == selected_page_table.end()) {
- UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
+ ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
return;
}
std::vector<ImageId>& image_ids = page_it->second;
const auto vector_it = std::ranges::find(image_ids, image_id);
if (vector_it == image_ids.end()) {
- UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
- page << PAGE_BITS);
+ ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
+ page << PAGE_BITS);
return;
}
image_ids.erase(vector_it);
@@ -1504,14 +1504,14 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
ForEachCPUPage(image.cpu_addr, image.guest_size_bytes, [this, map_id](u64 page) {
const auto page_it = page_table.find(page);
if (page_it == page_table.end()) {
- UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
+ ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
return;
}
std::vector<ImageMapId>& image_map_ids = page_it->second;
const auto vector_it = std::ranges::find(image_map_ids, map_id);
if (vector_it == image_map_ids.end()) {
- UNREACHABLE_MSG("Unregistering unregistered image in page=0x{:x}",
- page << PAGE_BITS);
+ ASSERT_MSG(false, "Unregistering unregistered image in page=0x{:x}",
+ page << PAGE_BITS);
return;
}
image_map_ids.erase(vector_it);
@@ -1532,7 +1532,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
ForEachCPUPage(cpu_addr, size, [this, image_id](u64 page) {
const auto page_it = page_table.find(page);
if (page_it == page_table.end()) {
- UNREACHABLE_MSG("Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
+ ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << PAGE_BITS);
return;
}
std::vector<ImageMapId>& image_map_ids = page_it->second;
@@ -1616,15 +1616,15 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
const GPUVAddr gpu_addr = image.gpu_addr;
const auto alloc_it = image_allocs_table.find(gpu_addr);
if (alloc_it == image_allocs_table.end()) {
- UNREACHABLE_MSG("Trying to delete an image alloc that does not exist in address 0x{:x}",
- gpu_addr);
+ ASSERT_MSG(false, "Trying to delete an image alloc that does not exist in address 0x{:x}",
+ gpu_addr);
return;
}
const ImageAllocId alloc_id = alloc_it->second;
std::vector<ImageId>& alloc_images = slot_image_allocs[alloc_id].images;
const auto alloc_image_it = std::ranges::find(alloc_images, image_id);
if (alloc_image_it == alloc_images.end()) {
- UNREACHABLE_MSG("Trying to delete an image that does not exist");
+ ASSERT_MSG(false, "Trying to delete an image that does not exist");
return;
}
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
BPP_CASE(16)
#undef BPP_CASE
default:
- UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
+ ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
@@ -209,7 +209,7 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
BPP_CASE(16)
#undef BPP_CASE
default:
- UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
+ ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
@@ -230,7 +230,7 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
BPP_CASE(16)
#undef BPP_CASE
default:
- UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
+ ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
@@ -253,7 +253,7 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
BPP_CASE(16)
#undef BPP_CASE
default:
- UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
+ ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
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
}
VkPhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR workgroup_layout;
- if (khr_workgroup_memory_explicit_layout) {
+ if (khr_workgroup_memory_explicit_layout && is_shader_int16_supported) {
workgroup_layout = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_FEATURES_KHR,
@@ -577,6 +577,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
.workgroupMemoryExplicitLayout16BitAccess = VK_TRUE,
};
SetNext(next, workgroup_layout);
+ } else if (khr_workgroup_memory_explicit_layout) {
+ // TODO(lat9nq): Find a proper fix for this
+ LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_workgroup_memory_explicit_layout due to a "
+ "yuzu bug when host driver does not support 16-bit integers");
+ khr_workgroup_memory_explicit_layout = false;
}
VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR executable_properties;
@@ -664,6 +669,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
const bool is_amd =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
if (is_amd) {
+ // TODO(lat9nq): Add an upper bound when AMD fixes their VK_KHR_push_descriptor
+ const bool has_broken_push_descriptor = VK_VERSION_MAJOR(properties.driverVersion) == 2 &&
+ VK_VERSION_MINOR(properties.driverVersion) == 0 &&
+ VK_VERSION_PATCH(properties.driverVersion) >= 226;
+ if (khr_push_descriptor && has_broken_push_descriptor) {
+ LOG_WARNING(
+ Render_Vulkan,
+ "Disabling AMD driver 2.0.226 and later from broken VK_KHR_push_descriptor");
+ khr_push_descriptor = false;
+ }
+
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
sets_per_pool = 96;
// 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
// The wanted format is not supported by hardware, search for alternatives
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
if (alternatives == nullptr) {
- UNREACHABLE_MSG("Format={} with usage={} and type={} has no defined alternatives and host "
- "hardware does not support it",
- wanted_format, wanted_usage, format_type);
+ ASSERT_MSG(false,
+ "Format={} with usage={} and type={} has no defined alternatives and host "
+ "hardware does not support it",
+ wanted_format, wanted_usage, format_type);
return wanted_format;
}
@@ -740,9 +757,10 @@ VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags
}
// No alternatives found, panic
- UNREACHABLE_MSG("Format={} with usage={} and type={} is not supported by the host hardware and "
- "doesn't support any of the alternatives",
- wanted_format, wanted_usage, format_type);
+ ASSERT_MSG(false,
+ "Format={} with usage={} and type={} is not supported by the host hardware and "
+ "doesn't support any of the alternatives",
+ wanted_format, wanted_usage, format_type);
return wanted_format;
}
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 @@
#include "common/dynamic_library.h"
#include "common/fs/path_util.h"
+#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_library.h"
namespace Vulkan {
Common::DynamicLibrary OpenLibrary() {
+ LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
Common::DynamicLibrary library;
#ifdef __APPLE__
// Check if a path to a specific Vulkan library has been specified.
@@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() {
}
#else
std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
+ LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
if (!library.Open(filename.c_str())) {
// Android devices may not have libvulkan.so.1, only libvulkan.so.
filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
+ LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
void(library.Open(filename.c_str()));
}
#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 {
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
}
- UNREACHABLE_MSG("Invalid memory usage={}", usage);
+ ASSERT_MSG(false, "Invalid memory usage={}", usage);
return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
@@ -325,7 +325,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask,
// Remove device local, if it's not supported by the requested resource
return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
- UNREACHABLE_MSG("No compatible memory types found");
+ ASSERT_MSG(false, "No compatible memory types found");
return 0;
}
@@ -349,7 +349,7 @@ bool IsHostVisible(MemoryUsage usage) noexcept {
case MemoryUsage::Download:
return true;
}
- UNREACHABLE_MSG("Invalid memory usage={}", usage);
+ ASSERT_MSG(false, "Invalid memory usage={}", usage);
return false;
}
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 {
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
case VkResult::VK_RESULT_MAX_ENUM:
return "VK_RESULT_MAX_ENUM";
+ case VkResult::VK_ERROR_COMPRESSION_EXHAUSTED_EXT:
+ return "VK_ERROR_COMPRESSION_EXHAUSTED_EXT";
}
return "Unknown";
}
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 {
namespace Telemetry = Common::Telemetry;
struct TelemetryJson::Impl {
- Impl(std::string host, std::string username, std::string token)
- : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
+ Impl(std::string host_, std::string username_, std::string token_)
+ : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {}
nlohmann::json& TopSection() {
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'};
constexpr std::size_t TIMEOUT_SECONDS = 30;
struct Client::Impl {
- Impl(std::string host, std::string username, std::string token)
- : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
+ Impl(std::string host_, std::string username_, std::string token_)
+ : host{std::move(host_)}, username{std::move(username_)}, token{std::move(token_)} {
std::scoped_lock lock{jwt_cache.mutex};
- if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
+ if (username == jwt_cache.username && token == jwt_cache.token) {
jwt = jwt_cache.jwt;
}
}
@@ -69,8 +69,8 @@ struct Client::Impl {
*/
WebResult GenericRequest(const std::string& method, const std::string& path,
const std::string& data, const std::string& accept,
- const std::string& jwt = "", const std::string& username = "",
- const std::string& token = "") {
+ const std::string& jwt_ = "", const std::string& username_ = "",
+ const std::string& token_ = "") {
if (cli == nullptr) {
cli = std::make_unique<httplib::Client>(host.c_str());
}
@@ -85,14 +85,14 @@ struct Client::Impl {
cli->set_write_timeout(TIMEOUT_SECONDS);
httplib::Headers params;
- if (!jwt.empty()) {
+ if (!jwt_.empty()) {
params = {
- {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
+ {std::string("Authorization"), fmt::format("Bearer {}", jwt_)},
};
- } else if (!username.empty()) {
+ } else if (!username_.empty()) {
params = {
- {std::string("x-username"), username},
- {std::string("x-token"), token},
+ {std::string("x-username"), username_},
+ {std::string("x-token"), token_},
};
}
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
applets/qt_web_browser_scripts.h
bootmanager.cpp
bootmanager.h
+ check_vulkan.cpp
+ check_vulkan.h
compatdb.ui
compatibility_list.cpp
compatibility_list.h
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
# Update source TS file if enabled
if (GENERATE_QT_TRANSLATION)
get_target_property(SRCS yuzu SOURCES)
- qt5_create_translation(QM_FILES
+ qt_create_translation(QM_FILES
${SRCS}
${UIS}
${YUZU_QT_LANGUAGES}/en.ts
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
# Compile TS files to QM files
- qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
+ qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
# Build a QRC file from the QM file list
set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
# Add the QRC file to package in all QM files
- qt5_add_resources(LANGUAGES ${LANGUAGES_QRC})
+ qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
else()
set(LANGUAGES)
endif()
@@ -236,18 +238,23 @@ if (APPLE)
set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
elseif(WIN32)
# compile as a win32 gui application instead of a console application
- target_link_libraries(yuzu PRIVATE Qt5::WinMain)
+ if (QT_VERSION VERSION_GREATER 6)
+ target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
+ else()
+ target_link_libraries(yuzu PRIVATE Qt5::WinMain)
+ endif()
if(MSVC)
+ target_link_libraries(yuzu PRIVATE version.lib)
set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
elseif(MINGW)
- set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-mwindows")
+ set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "-Wl,--subsystem,windows")
endif()
endif()
create_target_directory_groups(yuzu)
target_link_libraries(yuzu PRIVATE common core input_common video_core)
-target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets)
+target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -255,7 +262,7 @@ if (NOT WIN32)
target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
- target_link_libraries(yuzu PRIVATE Qt5::DBus)
+ target_link_libraries(yuzu PRIVATE Qt::DBus)
endif()
target_compile_definitions(yuzu PRIVATE
@@ -291,7 +298,7 @@ if (USE_DISCORD_PRESENCE)
endif()
if (YUZU_USE_QT_WEB_ENGINE)
- target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets)
+ target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
endif ()
@@ -319,3 +326,7 @@ endif()
if (NOT APPLE)
target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
endif()
+
+if (ARCHITECTURE_x86_64)
+ target_link_libraries(yuzu PRIVATE dynarmic)
+endif()
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)
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
ui->setupUi(this);
- ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200));
+ // Try and request the icon from Qt theme (Linux?)
+ const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
+ if (!yuzu_logo.isNull()) {
+ ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
+ }
ui->labelBuildInfo->setText(
ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
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 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="maximumSize">
+ <size>
+ <width>200</width>
+ <height>200</height>
+ </size>
+ </property>
<property name="text">
- <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>
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
</property>
</widget>
</item>
@@ -152,7 +164,7 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<resources>
- <include location="../../dist/icons/icons.qrc"/>
+ <include location="../../dist/qt_themes_default/default/default.qrc"/>
</resources>
<connections>
<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() {
switch (max_supported_players) {
case 0:
default:
- UNREACHABLE();
+ ASSERT(false);
return;
case 1:
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(
break;
}
- auto text = ui->topOSK->currentIndex() == 1
- ? ui->text_edit_osk->toPlainText().toStdU16String()
- : ui->line_edit_osk->text().toStdU16String();
+ const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
+ : ui->line_edit_osk->text();
+ auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
- emit SubmitNormalText(SwkbdResult::Ok, std::move(text), true);
+ emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str), true);
break;
}
}
@@ -562,7 +562,7 @@ void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
return;
}
- InlineTextInsertString(entered_text.toStdU16String());
+ InlineTextInsertString(Common::U16StringFromBuffer(entered_text.utf16(), entered_text.size()));
}
void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
@@ -1119,11 +1119,11 @@ void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button)
}
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
- auto text = ui->topOSK->currentIndex() == 1
- ? ui->text_edit_osk->toPlainText().toStdU16String()
- : ui->line_edit_osk->text().toStdU16String();
+ const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
+ : ui->line_edit_osk->text();
+ auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
- emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
+ emit SubmitNormalText(SwkbdResult::Ok, std::move(text_str));
return;
}
@@ -1189,7 +1189,8 @@ void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button)
return;
}
- InlineTextInsertString(button->text().toStdU16String());
+ const auto button_text = button->text();
+ InlineTextInsertString(Common::U16StringFromBuffer(button_text.utf16(), button_text.size()));
// Revert the keyboard to lowercase if the shift key is active.
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
@@ -1282,11 +1283,11 @@ void QtSoftwareKeyboardDialog::TranslateButtonPress(Core::HID::NpadButton button
if (is_inline) {
emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
} else {
- auto text = ui->topOSK->currentIndex() == 1
- ? ui->text_edit_osk->toPlainText().toStdU16String()
- : ui->line_edit_osk->text().toStdU16String();
+ const auto text = ui->topOSK->currentIndex() == 1 ? ui->text_edit_osk->toPlainText()
+ : ui->line_edit_osk->text();
+ auto text_str = Common::U16StringFromBuffer(text.utf16(), text.size());
- emit SubmitNormalText(SwkbdResult::Cancel, std::move(text));
+ emit SubmitNormalText(SwkbdResult::Cancel, std::move(text_str));
}
break;
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() {
auto& gpu = system.GPU();
auto stop_token = stop_source.get_token();
+ bool debugger_should_start = system.DebuggerEnabled();
system.RegisterHostThread();
@@ -89,6 +90,12 @@ void EmuThread::run() {
this->SetRunning(false);
emit ErrorThrown(result, system.GetStatusDetails());
}
+
+ if (debugger_should_start) {
+ system.InitializeDebugger();
+ debugger_should_start = false;
+ }
+
running_wait.Wait();
result = system.Pause();
if (result != Core::SystemResultStatus::Success) {
@@ -102,11 +109,9 @@ void EmuThread::run() {
was_active = true;
emit DebugModeEntered();
}
- } else if (exec_step) {
- UNIMPLEMENTED();
} else {
std::unique_lock lock{running_mutex};
- running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; });
+ running_cv.wait(lock, stop_token, [this] { return IsRunning(); });
}
}
@@ -122,7 +127,7 @@ void EmuThread::run() {
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
/// Create the original context that should be shared from
- explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
+ explicit OpenGLSharedContext(QSurface* surface_) : surface{surface_} {
QSurfaceFormat format;
format.setVersion(4, 6);
format.setProfile(QSurfaceFormat::CompatibilityProfile);
@@ -359,9 +364,9 @@ void GRenderWindow::RestoreGeometry() {
QWidget::restoreGeometry(geometry);
}
-void GRenderWindow::restoreGeometry(const QByteArray& geometry) {
+void GRenderWindow::restoreGeometry(const QByteArray& geometry_) {
// Make sure users of this class don't need to deal with backing up the geometry themselves
- QWidget::restoreGeometry(geometry);
+ QWidget::restoreGeometry(geometry_);
BackupGeometry();
}
@@ -747,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
- QCursor::setPos(mapToGlobal({center_x, center_y}));
+ QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
}
emit MouseActivity();
@@ -772,65 +777,25 @@ void GRenderWindow::wheelEvent(QWheelEvent* event) {
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
for (const auto& touch_point : touch_points) {
- if (!TouchUpdate(touch_point)) {
- TouchStart(touch_point);
- }
+ const auto [x, y] = ScaleTouch(touch_point.pos());
+ const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, touch_point.id());
}
}
void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
QList<QTouchEvent::TouchPoint> touch_points = event->touchPoints();
+ input_subsystem->GetTouchScreen()->ClearActiveFlag();
for (const auto& touch_point : touch_points) {
- if (!TouchUpdate(touch_point)) {
- TouchStart(touch_point);
- }
- }
- // Release all inactive points
- for (std::size_t id = 0; id < touch_ids.size(); ++id) {
- if (!TouchExist(touch_ids[id], touch_points)) {
- touch_ids[id] = 0;
- input_subsystem->GetTouchScreen()->TouchReleased(id);
- }
+ const auto [x, y] = ScaleTouch(touch_point.pos());
+ const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
+ input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, touch_point.id());
}
+ input_subsystem->GetTouchScreen()->ReleaseInactiveTouch();
}
void GRenderWindow::TouchEndEvent() {
- for (std::size_t id = 0; id < touch_ids.size(); ++id) {
- if (touch_ids[id] != 0) {
- touch_ids[id] = 0;
- input_subsystem->GetTouchScreen()->TouchReleased(id);
- }
- }
-}
-
-void GRenderWindow::TouchStart(const QTouchEvent::TouchPoint& touch_point) {
- for (std::size_t id = 0; id < touch_ids.size(); ++id) {
- if (touch_ids[id] == 0) {
- touch_ids[id] = touch_point.id() + 1;
- const auto [x, y] = ScaleTouch(touch_point.pos());
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
- input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id);
- }
- }
-}
-
-bool GRenderWindow::TouchUpdate(const QTouchEvent::TouchPoint& touch_point) {
- for (std::size_t id = 0; id < touch_ids.size(); ++id) {
- if (touch_ids[id] == static_cast<std::size_t>(touch_point.id() + 1)) {
- const auto [x, y] = ScaleTouch(touch_point.pos());
- const auto [touch_x, touch_y] = MapToTouchScreen(x, y);
- input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id);
- return true;
- }
- }
- return false;
-}
-
-bool GRenderWindow::TouchExist(std::size_t id,
- const QList<QTouchEvent::TouchPoint>& touch_points) const {
- return std::any_of(touch_points.begin(), touch_points.end(), [id](const auto& point) {
- return id == static_cast<std::size_t>(point.id() + 1);
- });
+ input_subsystem->GetTouchScreen()->ReleaseAllTouch();
}
bool GRenderWindow::event(QEvent* event) {
@@ -1049,8 +1014,8 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
return unsupported_ext;
}
-void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
- this->emu_thread = emu_thread;
+void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread_) {
+ emu_thread = emu_thread_;
}
void 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 @@
#include <mutex>
#include <QImage>
+#include <QStringList>
#include <QThread>
#include <QTouchEvent>
#include <QWidget>
@@ -20,7 +21,6 @@
class GRenderWindow;
class GMainWindow;
class QKeyEvent;
-class QStringList;
namespace Core {
enum class SystemResultStatus : u32;
@@ -55,22 +55,13 @@ public:
void run() override;
/**
- * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
- * @note This function is thread-safe
- */
- void ExecStep() {
- exec_step = true;
- running_cv.notify_all();
- }
-
- /**
* Sets whether the emulation thread is running or not
- * @param running Boolean value, set the emulation thread to running if true
+ * @param running_ Boolean value, set the emulation thread to running if true
* @note This function is thread-safe
*/
- void SetRunning(bool running) {
+ void SetRunning(bool running_) {
std::unique_lock lock{running_mutex};
- this->running = running;
+ running = running_;
lock.unlock();
running_cv.notify_all();
if (!running) {
@@ -99,7 +90,6 @@ public:
}
private:
- bool exec_step = false;
bool running = false;
std::stop_source stop_source;
std::mutex running_mutex;
@@ -148,8 +138,8 @@ public:
void BackupGeometry();
void RestoreGeometry();
- void restoreGeometry(const QByteArray& geometry); // overridden
- QByteArray saveGeometry(); // overridden
+ void restoreGeometry(const QByteArray& geometry_); // overridden
+ QByteArray saveGeometry(); // overridden
qreal windowPixelRatio() const;
@@ -199,7 +189,7 @@ public:
void Exit();
public slots:
- void OnEmulationStarting(EmuThread* emu_thread);
+ void OnEmulationStarting(EmuThread* emu_thread_);
void OnEmulationStopping();
void OnFramebufferSizeChanged();
@@ -217,10 +207,6 @@ private:
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
- void TouchStart(const QTouchEvent::TouchPoint& touch_point);
- bool TouchUpdate(const QTouchEvent::TouchPoint& touch_point);
- bool TouchExist(std::size_t id, const QList<QTouchEvent::TouchPoint>& touch_points) const;
-
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
bool InitializeOpenGL();
@@ -246,8 +232,6 @@ private:
bool first_frame = false;
InputCommon::TasInput::TasState last_tas_state;
- std::array<std::size_t, 16> touch_ids{};
-
Core::System& system;
protected:
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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "video_core/vulkan_common/vulkan_wrapper.h"
+
+#include <filesystem>
+#include <fstream>
+#include "common/fs/fs.h"
+#include "common/fs/path_util.h"
+#include "common/logging/log.h"
+#include "video_core/vulkan_common/vulkan_instance.h"
+#include "video_core/vulkan_common/vulkan_library.h"
+#include "yuzu/check_vulkan.h"
+#include "yuzu/uisettings.h"
+
+constexpr char TEMP_FILE_NAME[] = "vulkan_check";
+
+bool CheckVulkan() {
+ if (UISettings::values.has_broken_vulkan) {
+ return true;
+ }
+
+ LOG_DEBUG(Frontend, "Checking presence of Vulkan");
+
+ const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
+ const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
+
+ if (std::filesystem::exists(temp_file_loc)) {
+ LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
+
+ UISettings::values.has_broken_vulkan = true;
+ std::filesystem::remove(temp_file_loc);
+ return false;
+ }
+
+ std::ofstream temp_file_handle(temp_file_loc);
+ temp_file_handle.close();
+
+ try {
+ Vulkan::vk::InstanceDispatch dld;
+ const Common::DynamicLibrary library = Vulkan::OpenLibrary();
+ const Vulkan::vk::Instance instance =
+ Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
+
+ } catch (const Vulkan::vk::Exception& exception) {
+ LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
+ // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
+ // application, not when we can handle it.
+ }
+
+ std::filesystem::remove(temp_file_loc);
+ return true;
+}
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 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+bool 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{{
// UISetting::values.shortcuts, which is alphabetically ordered.
// clang-format off
const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
- {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
- {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
- {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
- {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
- {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
- {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
- {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
- {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
- {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
- {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
- {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
- {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
- {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
- {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
+ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
}};
// clang-format on
@@ -525,6 +525,9 @@ void Config::ReadDebuggingValues() {
// Intentionally not using the QT default setting as this is intended to be changed in the ini
Settings::values.record_frame_times =
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
+
+ ReadBasicSetting(Settings::values.use_gdbstub);
+ ReadBasicSetting(Settings::values.gdbstub_port);
ReadBasicSetting(Settings::values.program_args);
ReadBasicSetting(Settings::values.dump_exefs);
ReadBasicSetting(Settings::values.dump_nso);
@@ -679,6 +682,12 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
+ if (!global && UISettings::values.has_broken_vulkan &&
+ Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
+ !Settings::values.renderer_backend.UsingGlobal()) {
+ Settings::values.renderer_backend.SetGlobal(true);
+ }
+
if (global) {
ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -798,6 +807,7 @@ void Config::ReadUIValues() {
ReadBasicSetting(UISettings::values.pause_when_in_background);
ReadBasicSetting(UISettings::values.mute_when_in_background);
ReadBasicSetting(UISettings::values.hide_mouse);
+ ReadBasicSetting(UISettings::values.has_broken_vulkan);
ReadBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup();
@@ -1095,6 +1105,8 @@ void Config::SaveDebuggingValues() {
// Intentionally not using the QT default setting as this is intended to be changed in the ini
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
+ WriteBasicSetting(Settings::values.use_gdbstub);
+ WriteBasicSetting(Settings::values.gdbstub_port);
WriteBasicSetting(Settings::values.program_args);
WriteBasicSetting(Settings::values.dump_exefs);
WriteBasicSetting(Settings::values.dump_nso);
@@ -1343,6 +1355,7 @@ void Config::SaveUIValues() {
WriteBasicSetting(UISettings::values.pause_when_in_background);
WriteBasicSetting(UISettings::values.mute_when_in_background);
WriteBasicSetting(UISettings::values.hide_mouse);
+ WriteBasicSetting(UISettings::values.has_broken_vulkan);
WriteBasicSetting(UISettings::values.disable_web_applet);
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)
QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
+
+ connect(ui->toggle_gdbstub, &QCheckBox::toggled,
+ [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
}
ConfigureDebug::~ConfigureDebug() = default;
void ConfigureDebug::SetConfiguration() {
const bool runtime_lock = !system.IsPoweredOn();
-
+ ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
+ ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
+ ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
ui->toggle_console->setEnabled(runtime_lock);
ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
@@ -53,6 +58,8 @@ void ConfigureDebug::SetConfiguration() {
ui->enable_nsight_aftermath->setChecked(Settings::values.enable_nsight_aftermath.GetValue());
ui->dump_shaders->setEnabled(runtime_lock);
ui->dump_shaders->setChecked(Settings::values.dump_shaders.GetValue());
+ ui->dump_macros->setEnabled(runtime_lock);
+ ui->dump_macros->setChecked(Settings::values.dump_macros.GetValue());
ui->disable_macro_jit->setEnabled(runtime_lock);
ui->disable_macro_jit->setChecked(Settings::values.disable_macro_jit.GetValue());
ui->disable_loop_safety_checks->setEnabled(runtime_lock);
@@ -69,6 +76,8 @@ void ConfigureDebug::SetConfiguration() {
}
void ConfigureDebug::ApplyConfiguration() {
+ Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
+ Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
UISettings::values.show_console = ui->toggle_console->isChecked();
Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
@@ -83,6 +92,7 @@ void ConfigureDebug::ApplyConfiguration() {
Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked();
Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked();
Settings::values.dump_shaders = ui->dump_shaders->isChecked();
+ Settings::values.dump_macros = ui->dump_macros->isChecked();
Settings::values.disable_shader_loop_safety_checks =
ui->disable_loop_safety_checks->isChecked();
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 @@
<class>ConfigureDebug</class>
<widget class="QWidget" name="ConfigureDebug">
<layout class="QVBoxLayout" name="verticalLayout_1">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Debugger</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <item>
+ <widget class="QCheckBox" name="toggle_gdbstub">
+ <property name="text">
+ <string>Enable GDB Stub</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="gdbport_spinbox">
+ <property name="minimum">
+ <number>1024</number>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
@@ -118,6 +172,19 @@
</property>
</widget>
</item>
+ <item row="0" column="2">
+ <widget class="QCheckBox" name="dump_macros">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string>When checked, it will dump all the macro programs of the GPU</string>
+ </property>
+ <property name="text">
+ <string>Dump Maxwell Macros</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="1">
<widget class="QCheckBox" name="disable_macro_jit">
<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 @@
#include "yuzu/hotkeys.h"
#include "yuzu/uisettings.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
InputCommon::InputSubsystem* input_subsystem,
Core::System& system_)
- : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()},
- registry(registry), system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_,
- this)},
+ : QDialog(parent), ui{std::make_unique<Ui::ConfigureDialog>()}, registry{registry_},
+ system{system_}, audio_tab{std::make_unique<ConfigureAudio>(system_, this)},
cpu_tab{std::make_unique<ConfigureCpu>(system_, this)},
debug_tab_tab{std::make_unique<ConfigureDebugTab>(system_, this)},
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 {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
~ConfigureDialog() override;
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 @@
#include "video_core/vulkan_common/vulkan_library.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
+#include "yuzu/uisettings.h"
ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
@@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
UpdateBackgroundColorButton(new_bg_color);
});
+ connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
+ UISettings::values.has_broken_vulkan = false;
+
+ if (RetrieveVulkanDevices()) {
+ ui->api->setEnabled(true);
+ ui->button_check_vulkan->hide();
+
+ for (const auto& device : vulkan_devices) {
+ ui->device->addItem(device);
+ }
+ } else {
+ UISettings::values.has_broken_vulkan = true;
+ }
+ });
+
+ ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
+ ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
+
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
}
@@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
vulkan_device = Settings::values.vulkan_device.GetValue(true);
shader_backend = Settings::values.shader_backend.GetValue(true);
ui->device_widget->setEnabled(false);
- ui->backend_widget->setEnabled(false);
+ ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
} else {
vulkan_device = Settings::values.vulkan_device.GetValue();
shader_backend = Settings::values.shader_backend.GetValue();
@@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() {
}
}
-void ConfigureGraphics::RetrieveVulkanDevices() try {
+bool ConfigureGraphics::RetrieveVulkanDevices() try {
+ if (UISettings::values.has_broken_vulkan) {
+ return false;
+ }
+
using namespace Vulkan;
vk::InstanceDispatch dld;
@@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
vulkan_devices.push_back(QString::fromStdString(name));
}
+ return true;
} catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
+ return false;
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
ConfigurationShared::InsertGlobalItem(
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
+
+ if (UISettings::values.has_broken_vulkan) {
+ ui->backend_widget->setEnabled(true);
+ ConfigurationShared::SetColoredComboBox(
+ ui->backend, ui->backend_widget,
+ static_cast<int>(Settings::values.shader_backend.GetValue(true)));
+ }
}
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:
void UpdateDeviceSelection(int device);
void UpdateShaderBackendSelection(int backend);
- void RetrieveVulkanDevices();
+ bool RetrieveVulkanDevices();
void SetupPerGameUI();
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 @@
<rect>
<x>0</x>
<y>0</y>
- <width>437</width>
- <height>482</height>
+ <width>471</width>
+ <height>759</height>
</rect>
</property>
<property name="windowTitle">
@@ -171,11 +171,11 @@
</widget>
</item>
<item>
- <widget class="QCheckBox" name="accelerate_astc">
- <property name="text">
- <string>Accelerate ASTC texture decoding</string>
- </property>
- </widget>
+ <widget class="QCheckBox" name="accelerate_astc">
+ <property name="text">
+ <string>Accelerate ASTC texture decoding</string>
+ </property>
+ </widget>
</item>
<item>
<widget class="QWidget" name="nvdec_emulation_widget" native="true">
@@ -438,43 +438,43 @@
</widget>
</item>
<item>
- <widget class="QWidget" name="anti_aliasing_layout" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_7">
- <property name="leftMargin">
- <number>0</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>0</number>
+ <widget class="QWidget" name="anti_aliasing_layout" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="anti_aliasing_label">
+ <property name="text">
+ <string>Anti-Aliasing Method:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="anti_aliasing_combobox">
+ <item>
+ <property name="text">
+ <string>None</string>
</property>
- <property name="bottomMargin">
- <number>0</number>
+ </item>
+ <item>
+ <property name="text">
+ <string>FXAA</string>
</property>
- <item>
- <widget class="QLabel" name="anti_aliasing_label">
- <property name="text">
- <string>Anti-Aliasing Method:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="anti_aliasing_combobox">
- <item>
- <property name="text">
- <string>None</string>
- </property>
- </item>
- <item>
- <property name="text">
- <string>FXAA</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
<item>
<widget class="QWidget" name="bg_layout" native="true">
@@ -574,6 +574,13 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QPushButton" name="button_check_vulkan">
+ <property name="text">
+ <string>Check for Working Vulkan</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
<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;
void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
for (const auto& group : registry.hotkey_groups) {
- auto* parent_item = new QStandardItem(group.first);
+ auto* parent_item =
+ new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
parent_item->setEditable(false);
+ parent_item->setData(group.first);
for (const auto& hotkey : group.second) {
- auto* action = new QStandardItem(hotkey.first);
+ auto* action =
+ new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
auto* keyseq =
new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
action->setEditable(false);
+ action->setData(hotkey.first);
keyseq->setEditable(false);
controller_keyseq->setEditable(false);
parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
ui->retranslateUi(this);
model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
+ for (int key_id = 0; key_id < model->rowCount(); key_id++) {
+ QStandardItem* parent = model->item(key_id, 0);
+ parent->setText(
+ QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
+ for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
+ QStandardItem* action = parent->child(key_column_id, name_column);
+ action->setText(
+ QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
+ }
+ }
}
void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
const QStandardItem* controller_keyseq =
parent->child(key_column_id, controller_column);
for (auto& [group, sub_actions] : registry.hotkey_groups) {
- if (group != parent->text())
+ if (group != parent->data())
continue;
for (auto& [action_name, hotkey] : sub_actions) {
- if (action_name != action->text())
+ if (action_name != action->data())
continue;
hotkey.keyseq = QKeySequence(keyseq->text());
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,
return QObject::tr("[unknown]");
}
-ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
- QWidget* bottom_row,
+ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index_,
+ QWidget* bottom_row_,
InputCommon::InputSubsystem* input_subsystem_,
InputProfiles* profiles_, Core::HID::HIDCore& hid_core_,
- bool is_powered_on_, bool debug)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
- debug(debug), is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_},
- profiles(profiles_), timeout_timer(std::make_unique<QTimer>()),
- poll_timer(std::make_unique<QTimer>()), bottom_row(bottom_row), hid_core{hid_core_} {
+ bool is_powered_on_, bool debug_)
+ : QWidget(parent),
+ ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index{player_index_}, debug{debug_},
+ is_powered_on{is_powered_on_}, input_subsystem{input_subsystem_}, profiles(profiles_),
+ timeout_timer(std::make_unique<QTimer>()),
+ poll_timer(std::make_unique<QTimer>()), bottom_row{bottom_row_}, hid_core{hid_core_} {
if (player_index == 0) {
auto* emulated_controller_p1 =
hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
@@ -696,39 +697,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
UpdateControllerEnabledButtons();
UpdateControllerButtonNames();
UpdateMotionButtons();
- connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
- [this, player_index](int) {
- UpdateControllerAvailableButtons();
- UpdateControllerEnabledButtons();
- UpdateControllerButtonNames();
- UpdateMotionButtons();
- const Core::HID::NpadStyleIndex type =
- GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
-
- if (player_index == 0) {
- auto* emulated_controller_p1 =
- hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
- auto* emulated_controller_handheld =
- hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
- bool is_connected = emulated_controller->IsConnected(true);
-
- emulated_controller_p1->SetNpadStyleIndex(type);
- emulated_controller_handheld->SetNpadStyleIndex(type);
- if (is_connected) {
- if (type == Core::HID::NpadStyleIndex::Handheld) {
- emulated_controller_p1->Disconnect();
- emulated_controller_handheld->Connect(true);
- emulated_controller = emulated_controller_handheld;
- } else {
- emulated_controller_handheld->Disconnect();
- emulated_controller_p1->Connect(true);
- emulated_controller = emulated_controller_p1;
- }
- }
- ui->controllerFrame->SetController(emulated_controller);
+ connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
+ UpdateControllerAvailableButtons();
+ UpdateControllerEnabledButtons();
+ UpdateControllerButtonNames();
+ UpdateMotionButtons();
+ const Core::HID::NpadStyleIndex type =
+ GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+
+ if (player_index == 0) {
+ auto* emulated_controller_p1 =
+ hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
+ auto* emulated_controller_handheld =
+ hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
+ bool is_connected = emulated_controller->IsConnected(true);
+
+ emulated_controller_p1->SetNpadStyleIndex(type);
+ emulated_controller_handheld->SetNpadStyleIndex(type);
+ if (is_connected) {
+ if (type == Core::HID::NpadStyleIndex::Handheld) {
+ emulated_controller_p1->Disconnect();
+ emulated_controller_handheld->Connect(true);
+ emulated_controller = emulated_controller_handheld;
+ } else {
+ emulated_controller_handheld->Disconnect();
+ emulated_controller_p1->Connect(true);
+ emulated_controller = emulated_controller_p1;
}
- emulated_controller->SetNpadStyleIndex(type);
- });
+ }
+ ui->controllerFrame->SetController(emulated_controller);
+ }
+ emulated_controller->SetNpadStyleIndex(type);
+ });
connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
&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() {
&ConfigureMotionTouch::OnConfigureTouchCalibration);
connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
&ConfigureMotionTouch::OnConfigureTouchFromButton);
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
+ &ConfigureMotionTouch::ApplyConfiguration);
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
if (CanCloseDialog()) {
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 @@
</layout>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigureMotionTouch</receiver>
- <slot>ApplyConfiguration()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>20</x>
- <y>20</y>
- </hint>
- <hint type="destinationlabel">
- <x>20</x>
- <y>20</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</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 @@
#include "yuzu/uisettings.h"
#include "yuzu/util/util.h"
-ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id, const std::string& file_name,
+ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
Core::System& system_)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()),
- title_id(title_id), system{system_} {
+ : QDialog(parent),
+ ui(std::make_unique<Ui::ConfigurePerGame>()), title_id{title_id_}, system{system_} {
const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name));
const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
@@ -116,8 +116,8 @@ void ConfigurePerGame::HandleApplyButtonClicked() {
ApplyConfiguration();
}
-void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) {
- this->file = std::move(file);
+void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file_) {
+ file = std::move(file_);
LoadConfiguration();
}
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 {
public:
// Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263
- explicit ConfigurePerGame(QWidget* parent, u64 title_id, const std::string& file_name,
+ explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const std::string& file_name,
Core::System& system_);
~ConfigurePerGame() override;
/// Save all button configurations to settings file
void ApplyConfiguration();
- void LoadFromFile(FileSys::VirtualFile file);
+ void LoadFromFile(FileSys::VirtualFile file_);
private:
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() {
Settings::values.disabled_addons[title_id] = disabled_addons;
}
-void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) {
- this->file = std::move(file);
+void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file_) {
+ file = std::move(file_);
LoadConfiguration();
}
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:
/// Save all button configurations to settings file
void ApplyConfiguration();
- void LoadFromFile(FileSys::VirtualFile file);
+ void LoadFromFile(FileSys::VirtualFile file_);
void SetTitleId(u64 id);
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,
const std::string invert_str = invert_value ? "+" : "-";
param.Set("invert_x", invert_str);
emulated_device->SetRingParam(param);
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
- ++sub_button_id) {
- analog_map_buttons[sub_button_id]->setText(
- AnalogToText(param, analog_sub_buttons[sub_button_id]));
+ for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
+ ++sub_button_id2) {
+ analog_map_buttons[sub_button_id2]->setText(
+ AnalogToText(param, analog_sub_buttons[sub_button_id2]));
}
});
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() {
// Guard if during game and set to game-specific value
if (Settings::values.rng_seed.UsingGlobal()) {
if (ui->rng_seed_checkbox->isChecked()) {
- Settings::values.rng_seed.SetValue(
- ui->rng_seed_edit->text().toULongLong(nullptr, 16));
+ Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
case ConfigurationShared::CheckState::Off:
Settings::values.rng_seed.SetGlobal(false);
if (ui->rng_seed_checkbox->isChecked()) {
- Settings::values.rng_seed.SetValue(
- ui->rng_seed_edit->text().toULongLong(nullptr, 16));
+ Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
} else {
Settings::values.rng_seed.SetValue(std::nullopt);
}
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) {
}
ConfigureTouchFromButton::ConfigureTouchFromButton(
- QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps,
+ QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps_,
InputCommon::InputSubsystem* input_subsystem_, const int default_index)
: QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()),
- touch_maps(touch_maps), input_subsystem{input_subsystem_}, selected_index(default_index),
+ touch_maps{touch_maps_}, input_subsystem{input_subsystem_}, selected_index{default_index},
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
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 {
public:
explicit ConfigureTouchFromButton(QWidget* parent,
- const std::vector<Settings::TouchFromButtonMap>& touch_maps,
+ const std::vector<Settings::TouchFromButtonMap>& touch_maps_,
InputCommon::InputSubsystem* input_subsystem_,
int default_index = 0);
~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 {
return text;
}
-WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table,
+WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address_, const Kernel::KHandleTable& handle_table,
Core::System& system_)
- : mutex_address(mutex_address), system{system_} {
+ : mutex_address{mutex_address_}, system{system_} {
mutex_value = system.Memory().Read32(mutex_address);
owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask);
owner = handle_table.GetObject<Kernel::KThread>(owner_handle).GetPointerUnsafe();
@@ -140,8 +140,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons
return list;
}
-WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread, Core::System& system_)
- : thread(thread), system{system_} {}
+WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_)
+ : thread{thread_}, system{system_} {}
WaitTreeCallstack::~WaitTreeCallstack() = default;
QString WaitTreeCallstack::GetText() const {
@@ -171,8 +171,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons
}
WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(
- const Kernel::KSynchronizationObject& o, Core::System& system_)
- : object(o), system{system_} {}
+ const Kernel::KSynchronizationObject& object_, Core::System& system_)
+ : object{object_}, system{system_} {}
WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default;
WaitTreeExpandableItem::WaitTreeExpandableItem() = default;
@@ -380,8 +380,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
return list;
}
-WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object, Core::System& system_)
- : WaitTreeSynchronizationObject(object, system_) {}
+WaitTreeEvent::WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_)
+ : WaitTreeSynchronizationObject(object_, system_) {}
WaitTreeEvent::~WaitTreeEvent() = default;
WaitTreeThreadList::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:
class WaitTreeMutexInfo : public WaitTreeExpandableItem {
Q_OBJECT
public:
- explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table,
+ explicit WaitTreeMutexInfo(VAddr mutex_address_, const Kernel::KHandleTable& handle_table,
Core::System& system_);
~WaitTreeMutexInfo() override;
@@ -97,7 +97,7 @@ private:
class WaitTreeCallstack : public WaitTreeExpandableItem {
Q_OBJECT
public:
- explicit WaitTreeCallstack(const Kernel::KThread& thread, Core::System& system_);
+ explicit WaitTreeCallstack(const Kernel::KThread& thread_, Core::System& system_);
~WaitTreeCallstack() override;
QString GetText() const override;
@@ -112,7 +112,7 @@ private:
class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {
Q_OBJECT
public:
- explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object,
+ explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object_,
Core::System& system_);
~WaitTreeSynchronizationObject() override;
@@ -162,7 +162,7 @@ private:
class WaitTreeEvent : public WaitTreeSynchronizationObject {
Q_OBJECT
public:
- explicit WaitTreeEvent(const Kernel::KReadableEvent& object, Core::System& system_);
+ explicit WaitTreeEvent(const Kernel::KReadableEvent& object_, Core::System& system_);
~WaitTreeEvent() override;
};
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 @@
#include "yuzu/uisettings.h"
#include "yuzu/util/controller_navigation.h"
-GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
- : QObject(parent), gamelist{gamelist} {}
+GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist_, QObject* parent)
+ : QObject(parent), gamelist{gamelist_} {}
// EventFilter in order to process systemkeys while editing the searchfield
bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) {
@@ -80,9 +80,9 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
return QObject::eventFilter(obj, event);
}
-void GameListSearchField::setFilterResult(int visible, int total) {
- this->visible = visible;
- this->total = total;
+void GameListSearchField::setFilterResult(int visible_, int total_) {
+ visible = visible_;
+ total = total_;
label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
}
@@ -309,9 +309,9 @@ void GameList::OnFilterCloseClicked() {
main_window->filterBarSetChecked(false);
}
-GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvider* provider,
+GameList::GameList(FileSys::VirtualFilesystem vfs_, FileSys::ManualContentProvider* provider_,
Core::System& system_, GMainWindow* parent)
- : QWidget{parent}, vfs(std::move(vfs)), provider(provider), system{system_} {
+ : QWidget{parent}, vfs{std::move(vfs_)}, provider{provider_}, system{system_} {
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &GameList::RefreshGameDirectory);
@@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
// Also artificially caps the watcher to a certain number of directories
constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
constexpr int SLICE_SIZE = 25;
- int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES);
+ int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
for (int i = 0; i < len; i += SLICE_SIZE) {
watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE));
QCoreApplication::processEvents();
@@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent}
layout->setAlignment(Qt::AlignCenter);
image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
- text->setText(tr("Double-click to add a new folder to the game list"));
+ RetranslateUI();
QFont font = text->font();
font.setPointSize(20);
text->setFont(font);
@@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() {
void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
emit GameListPlaceholder::AddDirectory();
}
+
+void GameListPlaceholder::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void GameListPlaceholder::RetranslateUI() {
+ text->setText(tr("Double-click to add a new folder to the game list"));
+}
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:
COLUMN_COUNT, // Number of columns
};
- explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs,
- FileSys::ManualContentProvider* provider, Core::System& system_,
+ explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
+ FileSys::ManualContentProvider* provider_, Core::System& system_,
GMainWindow* parent = nullptr);
~GameList() override;
@@ -166,6 +166,9 @@ protected:
void mouseDoubleClickEvent(QMouseEvent* event) override;
private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
QVBoxLayout* layout = nullptr;
QLabel* image = nullptr;
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:
static constexpr int GameDirRole = Qt::UserRole + 2;
explicit GameListDir(UISettings::GameDir& directory,
- GameListItemType dir_type = GameListItemType::CustomDir)
- : dir_type{dir_type} {
+ GameListItemType dir_type_ = GameListItemType::CustomDir)
+ : dir_type{dir_type_} {
setData(type(), TypeRole);
UISettings::GameDir* game_dir = &directory;
@@ -348,7 +348,7 @@ public:
explicit GameListSearchField(GameList* parent = nullptr);
QString filterText() const;
- void setFilterResult(int visible, int total);
+ void setFilterResult(int visible_, int total_);
void clear();
void setFocus();
@@ -356,7 +356,7 @@ public:
private:
class KeyReleaseEater : public QObject {
public:
- explicit KeyReleaseEater(GameList* gamelist, QObject* parent = nullptr);
+ explicit KeyReleaseEater(GameList* gamelist_, QObject* parent = nullptr);
private:
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
}
} // Anonymous namespace
-GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs,
- FileSys::ManualContentProvider* provider,
- QVector<UISettings::GameDir>& game_dirs,
- const CompatibilityList& compatibility_list, Core::System& system_)
- : vfs(std::move(vfs)), provider(provider), game_dirs(game_dirs),
- compatibility_list(compatibility_list), system{system_} {}
+GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_,
+ FileSys::ManualContentProvider* provider_,
+ QVector<UISettings::GameDir>& game_dirs_,
+ const CompatibilityList& compatibility_list_, Core::System& system_)
+ : vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_},
+ compatibility_list{compatibility_list_}, system{system_} {}
GameListWorker::~GameListWorker() = default;
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 {
Q_OBJECT
public:
- explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs,
- FileSys::ManualContentProvider* provider,
- QVector<UISettings::GameDir>& game_dirs,
- const CompatibilityList& compatibility_list, Core::System& system_);
+ explicit GameListWorker(std::shared_ptr<FileSys::VfsFilesystem> vfs_,
+ FileSys::ManualContentProvider* provider_,
+ QVector<UISettings::GameDir>& game_dirs_,
+ const CompatibilityList& compatibility_list_, Core::System& system_);
~GameListWorker() override;
/// 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
void LoadingScreen::paintEvent(QPaintEvent* event) {
QStyleOption opt;
- opt.init(this);
+ opt.initFrom(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
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 @@
#include <memory>
#include <QString>
#include <QWidget>
+#include <QtGlobal>
#if !QT_CONFIG(movie)
#define YUZU_QT_MOVIE_MISSING 1
@@ -88,4 +89,6 @@ private:
std::size_t slow_shader_first_value = 0;
};
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
+#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
#define QT_NO_OPENGL
#include <QClipboard>
#include <QDesktopServices>
-#include <QDesktopWidget>
#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
@@ -60,6 +59,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QProgressBar>
#include <QProgressDialog>
#include <QPushButton>
+#include <QScreen>
#include <QShortcut>
#include <QStatusBar>
#include <QString>
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
#include "yuzu/bootmanager.h"
+#include "yuzu/check_vulkan.h"
#include "yuzu/compatdb.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/configuration/config.h"
@@ -198,6 +199,59 @@ static void RemoveCachedContents() {
Common::FS::RemoveDirRecursively(offline_system_data);
}
+static void LogRuntimes() {
+#ifdef _MSC_VER
+ // It is possible that the name of the dll will change.
+ // vcruntime140.dll is for 2015 and onwards
+ constexpr char runtime_dll_name[] = "vcruntime140.dll";
+ UINT sz = GetFileVersionInfoSizeA(runtime_dll_name, nullptr);
+ bool runtime_version_inspection_worked = false;
+ if (sz > 0) {
+ std::vector<u8> buf(sz);
+ if (GetFileVersionInfoA(runtime_dll_name, 0, sz, buf.data())) {
+ VS_FIXEDFILEINFO* pvi;
+ sz = sizeof(VS_FIXEDFILEINFO);
+ if (VerQueryValueA(buf.data(), "\\", reinterpret_cast<LPVOID*>(&pvi), &sz)) {
+ if (pvi->dwSignature == VS_FFI_SIGNATURE) {
+ runtime_version_inspection_worked = true;
+ LOG_INFO(Frontend, "MSVC Compiler: {} Runtime: {}.{}.{}.{}", _MSC_VER,
+ pvi->dwProductVersionMS >> 16, pvi->dwProductVersionMS & 0xFFFF,
+ pvi->dwProductVersionLS >> 16, pvi->dwProductVersionLS & 0xFFFF);
+ }
+ }
+ }
+ }
+ if (!runtime_version_inspection_worked) {
+ LOG_INFO(Frontend, "Unable to inspect {}", runtime_dll_name);
+ }
+#endif
+}
+
+static QString PrettyProductName() {
+#ifdef _WIN32
+ // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
+ // With that notation change they changed the registry key used to denote the current version
+ QSettings windows_registry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
+ QSettings::NativeFormat);
+ const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
+ if (release_id == QStringLiteral("2009")) {
+ const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
+ const QString display_version =
+ windows_registry.value(QStringLiteral("DisplayVersion")).toString();
+ const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
+ u32 version = 10;
+ if (current_build >= 22000) {
+ version = 11;
+ }
+ return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
+ .arg(QString::number(version), display_version, QString::number(current_build),
+ QString::number(ubr));
+ }
+#endif
+ return QSysInfo::prettyProductName();
+}
+
GMainWindow::GMainWindow()
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -243,6 +297,7 @@ GMainWindow::GMainWindow()
const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
LOG_INFO(Frontend, "yuzu Version: {}", yuzu_build_version);
+ LogRuntimes();
#ifdef ARCHITECTURE_x86_64
const auto& caps = Common::GetCPUCaps();
std::string cpu_string = caps.cpu_string;
@@ -259,7 +314,7 @@ GMainWindow::GMainWindow()
}
LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
#endif
- LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
+ LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -297,6 +352,23 @@ GMainWindow::GMainWindow()
MigrateConfigFiles();
+ if (!CheckVulkan()) {
+ config->Save();
+
+ QMessageBox::warning(
+ this, tr("Broken Vulkan Installation Detected"),
+ tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
+ "href='https://yuzu-emu.org/wiki/faq/"
+ "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
+ "instructions to fix the issue</a>."));
+ }
+ if (UISettings::values.has_broken_vulkan) {
+ Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
+
+ renderer_status_button->setDisabled(true);
+ renderer_status_button->setChecked(false);
+ }
+
#if defined(HAVE_SDL2) && !defined(_WIN32)
SDL_InitSubSystem(SDL_INIT_VIDEO);
// SDL disables the screen saver by default, and setting the hint
@@ -827,12 +899,11 @@ void GMainWindow::InitializeWidgets() {
// Setup Dock button
dock_status_button = new QPushButton();
- dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
+ dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
dock_status_button->setFocusPolicy(Qt::NoFocus);
connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
- dock_status_button->setText(tr("DOCK"));
dock_status_button->setCheckable(true);
- dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
+ UpdateDockedButton();
statusBar()->insertPermanentWidget(0, dock_status_button);
gpu_accuracy_button = new QPushButton();
@@ -863,8 +934,7 @@ void GMainWindow::InitializeWidgets() {
Settings::values.renderer_backend.SetValue(Settings::RendererBackend::Vulkan);
} else {
Settings::values.renderer_backend.SetValue(Settings::RendererBackend::OpenGL);
- const auto filter = Settings::values.scaling_filter.GetValue();
- if (filter == Settings::ScalingFilter::Fsr) {
+ if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) {
Settings::values.scaling_filter.SetValue(Settings::ScalingFilter::NearestNeighbor);
UpdateFilterText();
}
@@ -1002,7 +1072,7 @@ void GMainWindow::InitializeHotkeys() {
void GMainWindow::SetDefaultUIGeometry() {
// geometry: 53% of the window contents are in the upper screen half, 47% in the lower half
- const QRect screenRect = QApplication::desktop()->screenGeometry(this);
+ const QRect screenRect = QGuiApplication::primaryScreen()->geometry();
const int w = screenRect.width() * 2 / 3;
const int h = screenRect.height() * 2 / 3;
@@ -1015,6 +1085,10 @@ void GMainWindow::SetDefaultUIGeometry() {
void GMainWindow::RestoreUIState() {
setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
restoreGeometry(UISettings::values.geometry);
+ // Work-around because the games list isn't supposed to be full screen
+ if (isFullScreen()) {
+ showNormal();
+ }
restoreState(UISettings::values.state);
render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -1367,7 +1441,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
}
return false;
}
- game_path = filename;
+ current_game_path = filename;
system->TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "Qt");
return true;
@@ -1401,7 +1475,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
type == StartGameType::Normal) {
// Load per game settings
- const auto file_path = std::filesystem::path{filename.toStdU16String()};
+ const auto file_path =
+ std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())};
const auto config_file_name = title_id == 0
? Common::FS::PathToUTF8String(file_path.filename())
: fmt::format("{:016X}", title_id);
@@ -1432,7 +1507,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
// Register an ExecuteProgram callback such that Core can execute a sub-program
system->RegisterExecuteProgramCallback(
- [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
+ [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); });
// Register an Exit callback such that Core can exit the currently running application.
system->RegisterExitCallback([this]() { render_window->Exit(); });
@@ -1482,7 +1557,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
}
if (res != Loader::ResultStatus::Success || title_name.empty()) {
title_name = Common::FS::PathToUTF8String(
- std::filesystem::path{filename.toStdU16String()}.filename());
+ std::filesystem::path{Common::U16StringFromBuffer(filename.utf16(), filename.size())}
+ .filename());
}
const bool is_64bit = system->Kernel().CurrentProcess()->Is64BitProcess();
const auto instruction_set_suffix = is_64bit ? tr("(64-bit)") : tr("(32-bit)");
@@ -1514,6 +1590,7 @@ void GMainWindow::ShutdownGame() {
AllowOSSleep();
+ system->DetachDebugger();
discord_rpc->Pause();
emu_thread->RequestStop();
@@ -1561,9 +1638,9 @@ void GMainWindow::ShutdownGame() {
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
emu_frametime_label->setVisible(false);
- renderer_status_button->setEnabled(true);
+ renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
- game_path.clear();
+ current_game_path.clear();
// When closing the game, destroy the GLWindow to clear the context after the game is closed
render_window->ReleaseRenderTarget();
@@ -1581,7 +1658,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
void GMainWindow::UpdateRecentFiles() {
const int num_recent_files =
- std::min(UISettings::values.recent_files.size(), max_recent_files_item);
+ std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
for (int i = 0; i < num_recent_files; i++) {
const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2482,7 +2559,7 @@ void GMainWindow::OnRestartGame() {
return;
}
// Make a copy since BootGame edits game_path
- BootGame(QString(game_path));
+ BootGame(QString(current_game_path));
}
void GMainWindow::OnPauseGame() {
@@ -2579,6 +2656,18 @@ void GMainWindow::ToggleFullscreen() {
}
}
+// We're going to return the screen that the given window has the most pixels on
+static QScreen* GuessCurrentScreen(QWidget* window) {
+ const QList<QScreen*> screens = QGuiApplication::screens();
+ return *std::max_element(
+ screens.cbegin(), screens.cend(), [window](const QScreen* left, const QScreen* right) {
+ const QSize left_size = left->geometry().intersected(window->geometry()).size();
+ const QSize right_size = right->geometry().intersected(window->geometry()).size();
+ return (left_size.height() * left_size.width()) <
+ (right_size.height() * right_size.width());
+ });
+}
+
void GMainWindow::ShowFullscreen() {
const auto show_fullscreen = [](QWidget* window) {
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) {
@@ -2587,7 +2676,7 @@ void GMainWindow::ShowFullscreen() {
}
window->hide();
window->setWindowFlags(window->windowFlags() | Qt::FramelessWindowHint);
- const auto screen_geometry = QApplication::desktop()->screenGeometry(window);
+ const auto screen_geometry = GuessCurrentScreen(window)->geometry();
window->setGeometry(screen_geometry.x(), screen_geometry.y(), screen_geometry.width(),
screen_geometry.height() + 1);
window->raise();
@@ -2771,6 +2860,10 @@ void GMainWindow::OnConfigure() {
mouse_hide_timer.start();
}
+ if (!UISettings::values.has_broken_vulkan) {
+ renderer_status_button->setEnabled(!emulation_running);
+ }
+
UpdateStatusButtons();
controller_dialog->refreshConfiguration();
}
@@ -2856,7 +2949,7 @@ void GMainWindow::OnToggleDockedMode() {
}
Settings::values.use_docked_mode.SetValue(!is_docked);
- dock_status_button->setChecked(!is_docked);
+ UpdateDockedButton();
OnDockedModeChanged(is_docked, !is_docked, *system);
}
@@ -2895,7 +2988,7 @@ void GMainWindow::OnToggleAdaptingFilter() {
void GMainWindow::OnConfigurePerGame() {
const u64 title_id = system->GetCurrentProcessProgramID();
- OpenPerGameConfiguration(title_id, game_path.toStdString());
+ OpenPerGameConfiguration(title_id, current_game_path.toStdString());
}
void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file_name) {
@@ -3150,7 +3243,7 @@ void GMainWindow::OnTasStateChanged() {
}
void GMainWindow::UpdateStatusBar() {
- if (emu_thread == nullptr) {
+ if (emu_thread == nullptr || !system->IsPoweredOn()) {
status_bar_update_timer.stop();
return;
}
@@ -3222,6 +3315,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
}
}
+void GMainWindow::UpdateDockedButton() {
+ const bool is_docked = Settings::values.use_docked_mode.GetValue();
+ dock_status_button->setChecked(is_docked);
+ dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
+}
+
void GMainWindow::UpdateFilterText() {
const auto filter = Settings::values.scaling_filter.GetValue();
switch (filter) {
@@ -3265,10 +3364,10 @@ void GMainWindow::UpdateAAText() {
}
void GMainWindow::UpdateStatusButtons() {
- dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
UpdateGPUAccuracyButton();
+ UpdateDockedButton();
UpdateFilterText();
UpdateAAText();
}
@@ -3319,7 +3418,7 @@ void GMainWindow::CenterMouseCursor() {
const int center_x = render_window->width() / 2;
const int center_y = render_window->height() / 2;
- QCursor::setPos(mapToGlobal({center_x, center_y}));
+ QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
}
void 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:
void MigrateConfigFiles();
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
std::string_view gpu_vendor = {});
+ void UpdateDockedButton();
void UpdateFilterText();
void UpdateAAText();
void UpdateStatusBar();
@@ -368,7 +369,7 @@ private:
bool emulation_running = false;
std::unique_ptr<EmuThread> emu_thread;
// The path to the game currently running
- QString game_path;
+ QString current_game_path;
bool auto_paused = false;
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 {
Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
+ // Set when Vulkan is known to crash the application
+ Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
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() {
ReadSetting("Debugging", Settings::values.use_debug_asserts);
ReadSetting("Debugging", Settings::values.use_auto_stub);
ReadSetting("Debugging", Settings::values.disable_macro_jit);
+ ReadSetting("Debugging", Settings::values.use_gdbstub);
+ ReadSetting("Debugging", Settings::values.gdbstub_port);
const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
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 =
[Renderer]
# Which backend API to use.
-# 0 (default): OpenGL, 1: Vulkan
+# 0: OpenGL, 1 (default): Vulkan
backend =
# Enable graphics API debugging mode.
@@ -437,6 +437,11 @@ disable_macro_jit=false
# Presents guest frames as they become available. Experimental.
# false: Disabled (default), true: Enabled
disable_fps_limit=false
+# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
+# false: Disabled (default), true: Enabled
+use_gdbstub=false
+# The port to use for the GDB server, if it is enabled.
+gdbstub_port=6543
[WebService]
# 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) {
}
void EmuWindow_SDL2::OnFingerUp() {
- input_subsystem->GetTouchScreen()->TouchReleased(0);
+ input_subsystem->GetTouchScreen()->ReleaseAllTouch();
}
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
@@ -162,7 +162,15 @@ void EmuWindow_SDL2::WaitEvent() {
SDL_Event event;
if (!SDL_WaitEvent(&event)) {
- LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", SDL_GetError());
+ const char* error = SDL_GetError();
+ if (!error || strcmp(error, "") == 0) {
+ // https://github.com/libsdl-org/SDL/issues/5780
+ // Sometimes SDL will return without actually having hit an error condition;
+ // just ignore it in this case.
+ return;
+ }
+
+ LOG_CRITICAL(Frontend, "SDL_WaitEvent failed: {}", error);
exit(1);
}
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;
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
- explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem, Core::System& system_);
+ explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_);
~EmuWindow_SDL2();
/// 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() {
return unsupported_ext.empty();
}
-EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem,
+EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen)
- : EmuWindow_SDL2{input_subsystem, system_} {
+ : EmuWindow_SDL2{input_subsystem_, system_} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
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;
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, Core::System& system_,
+ explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_,
bool fullscreen);
~EmuWindow_SDL2_GL();
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 @@
#include <SDL.h>
#include <SDL_syswm.h>
-EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem,
+EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_,
Core::System& system_, bool fullscreen)
- : EmuWindow_SDL2{input_subsystem, system_} {
+ : EmuWindow_SDL2{input_subsystem_, system_} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
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;
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem, Core::System& system,
+ explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem_, Core::System& system,
bool fullscreen);
~EmuWindow_SDL2_VK() override;
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) {
[](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
}
+ system.RegisterExitCallback([&] {
+ // Just exit right away.
+ exit(0);
+ });
+
void(system.Run());
+ if (system.DebuggerEnabled()) {
+ system.InitializeDebugger();
+ }
while (emu_window->IsOpen()) {
emu_window->WaitEvent();
}
+ system.DetachDebugger();
void(system.Pause());
system.Shutdown();