summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/command_generator.cpp16
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/bounded_threadsafe_queue.h180
-rw-r--r--src/common/elf.h333
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h4
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/arm/arm_interface.cpp46
-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/symbols.cpp85
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h18
-rw-r--r--src/core/debugger/debugger.cpp269
-rw-r--r--src/core/debugger/debugger.h41
-rw-r--r--src/core/debugger/debugger_interface.h79
-rw-r--r--src/core/debugger/gdbstub.cpp618
-rw-r--r--src/core/debugger/gdbstub.h48
-rw-r--r--src/core/debugger/gdbstub_arch.cpp483
-rw-r--r--src/core/debugger/gdbstub_arch.h67
-rw-r--r--src/core/hid/hid_types.h43
-rw-r--r--src/core/hle/kernel/k_process.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.h25
-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.cpp648
-rw-r--r--src/core/hle/service/hid/controllers/npad.h75
-rw-r--r--src/core/hle/service/hid/errors.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp236
-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/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_slot.h1
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp7
-rw-r--r--src/core/loader/elf.cpp183
-rw-r--r--src/core/memory.cpp13
-rw-r--r--src/core/memory.h11
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp8
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/gpu_thread.cpp3
-rw-r--r--src/video_core/gpu_thread.h6
-rw-r--r--src/video_core/surface.h6
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt24
-rw-r--r--src/yuzu/about_dialog.cpp6
-rw-r--r--src/yuzu/aboutdialog.ui16
-rw-r--r--src/yuzu/bootmanager.cpp13
-rw-r--r--src/yuzu/bootmanager.h12
-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.cpp9
-rw-r--r--src/yuzu/configuration/configure_debug.ui54
-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_motion_touch.cpp2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui19
-rw-r--r--src/yuzu/configuration/configure_system.cpp6
-rw-r--r--src/yuzu/game_list.cpp16
-rw-r--r--src/yuzu/game_list.h3
-rw-r--r--src/yuzu/loading_screen.cpp2
-rw-r--r--src/yuzu/loading_screen.h3
-rw-r--r--src/yuzu/main.cpp76
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/default_ini.h2
77 files changed, 3908 insertions, 900 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index ae4efafb6..ff20ed00f 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -129,17 +129,17 @@ s32 ToS32(float sample) {
129 return static_cast<s32>(rescaled_sample); 129 return static_cast<s32>(rescaled_sample);
130} 130}
131 131
132constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 132constexpr std::array<u8, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
134 134
135constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 135constexpr std::array<u8, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
136 1, 1, 1, 0, 0, 0, 0, 1, 1, 1}; 136 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
137 137
138constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 138constexpr std::array<u8, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
139 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; 139 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
140 140
141constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2, 141constexpr std::array<u8, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
142 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; 142 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
143 143
144template <std::size_t CHANNEL_COUNT> 144template <std::size_t CHANNEL_COUNT>
145void ApplyReverbGeneric( 145void ApplyReverbGeneric(
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index adf70eb8b..73bf626d4 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -58,6 +58,7 @@ add_library(common STATIC
58 div_ceil.h 58 div_ceil.h
59 dynamic_library.cpp 59 dynamic_library.cpp
60 dynamic_library.h 60 dynamic_library.h
61 elf.h
61 error.cpp 62 error.cpp
62 error.h 63 error.h
63 expected.h 64 expected.h
diff --git a/src/common/bounded_threadsafe_queue.h b/src/common/bounded_threadsafe_queue.h
new file mode 100644
index 000000000..e83064c7f
--- /dev/null
+++ b/src/common/bounded_threadsafe_queue.h
@@ -0,0 +1,180 @@
1// SPDX-FileCopyrightText: Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
2// SPDX-License-Identifier: MIT
3#pragma once
4#ifdef _MSC_VER
5#pragma warning(push)
6#pragma warning(disable : 4324)
7#endif
8
9#include <atomic>
10#include <bit>
11#include <condition_variable>
12#include <memory>
13#include <mutex>
14#include <new>
15#include <stdexcept>
16#include <stop_token>
17#include <type_traits>
18#include <utility>
19
20namespace Common {
21namespace mpsc {
22#if defined(__cpp_lib_hardware_interference_size)
23constexpr size_t hardware_interference_size = std::hardware_destructive_interference_size;
24#else
25constexpr size_t hardware_interference_size = 64;
26#endif
27
28template <typename T>
29using AlignedAllocator = std::allocator<T>;
30
31template <typename T>
32struct Slot {
33 ~Slot() noexcept {
34 if (turn.test()) {
35 destroy();
36 }
37 }
38
39 template <typename... Args>
40 void construct(Args&&... args) noexcept {
41 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
42 "T must be nothrow constructible with Args&&...");
43 std::construct_at(reinterpret_cast<T*>(&storage), std::forward<Args>(args)...);
44 }
45
46 void destroy() noexcept {
47 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
48 std::destroy_at(reinterpret_cast<T*>(&storage));
49 }
50
51 T&& move() noexcept {
52 return reinterpret_cast<T&&>(storage);
53 }
54
55 // Align to avoid false sharing between adjacent slots
56 alignas(hardware_interference_size) std::atomic_flag turn{};
57 struct aligned_store {
58 struct type {
59 alignas(T) unsigned char data[sizeof(T)];
60 };
61 };
62 typename aligned_store::type storage;
63};
64
65template <typename T, typename Allocator = AlignedAllocator<Slot<T>>>
66class Queue {
67public:
68 explicit Queue(const size_t capacity, const Allocator& allocator = Allocator())
69 : allocator_(allocator) {
70 if (capacity < 1) {
71 throw std::invalid_argument("capacity < 1");
72 }
73 // Ensure that the queue length is an integer power of 2
74 // This is so that idx(i) can be a simple i & mask_ insted of i % capacity
75 // https://github.com/rigtorp/MPMCQueue/pull/36
76 if (!std::has_single_bit(capacity)) {
77 throw std::invalid_argument("capacity must be an integer power of 2");
78 }
79
80 mask_ = capacity - 1;
81
82 // Allocate one extra slot to prevent false sharing on the last slot
83 slots_ = allocator_.allocate(mask_ + 2);
84 // Allocators are not required to honor alignment for over-aligned types
85 // (see http://eel.is/c++draft/allocator.requirements#10) so we verify
86 // alignment here
87 if (reinterpret_cast<uintptr_t>(slots_) % alignof(Slot<T>) != 0) {
88 allocator_.deallocate(slots_, mask_ + 2);
89 throw std::bad_alloc();
90 }
91 for (size_t i = 0; i < mask_ + 1; ++i) {
92 std::construct_at(&slots_[i]);
93 }
94 static_assert(alignof(Slot<T>) == hardware_interference_size,
95 "Slot must be aligned to cache line boundary to prevent false sharing");
96 static_assert(sizeof(Slot<T>) % hardware_interference_size == 0,
97 "Slot size must be a multiple of cache line size to prevent "
98 "false sharing between adjacent slots");
99 static_assert(sizeof(Queue) % hardware_interference_size == 0,
100 "Queue size must be a multiple of cache line size to "
101 "prevent false sharing between adjacent queues");
102 }
103
104 ~Queue() noexcept {
105 for (size_t i = 0; i < mask_ + 1; ++i) {
106 slots_[i].~Slot();
107 }
108 allocator_.deallocate(slots_, mask_ + 2);
109 }
110
111 // non-copyable and non-movable
112 Queue(const Queue&) = delete;
113 Queue& operator=(const Queue&) = delete;
114
115 void Push(const T& v) noexcept {
116 static_assert(std::is_nothrow_copy_constructible_v<T>,
117 "T must be nothrow copy constructible");
118 emplace(v);
119 }
120
121 template <typename P, typename = std::enable_if_t<std::is_nothrow_constructible_v<T, P&&>>>
122 void Push(P&& v) noexcept {
123 emplace(std::forward<P>(v));
124 }
125
126 void Pop(T& v, std::stop_token stop) noexcept {
127 auto const tail = tail_.fetch_add(1);
128 auto& slot = slots_[idx(tail)];
129 if (false == slot.turn.test()) {
130 std::unique_lock lock{cv_mutex};
131 cv.wait(lock, stop, [&slot] { return slot.turn.test(); });
132 }
133 v = slot.move();
134 slot.destroy();
135 slot.turn.clear();
136 slot.turn.notify_one();
137 }
138
139private:
140 template <typename... Args>
141 void emplace(Args&&... args) noexcept {
142 static_assert(std::is_nothrow_constructible_v<T, Args&&...>,
143 "T must be nothrow constructible with Args&&...");
144 auto const head = head_.fetch_add(1);
145 auto& slot = slots_[idx(head)];
146 slot.turn.wait(true);
147 slot.construct(std::forward<Args>(args)...);
148 slot.turn.test_and_set();
149 cv.notify_one();
150 }
151
152 constexpr size_t idx(size_t i) const noexcept {
153 return i & mask_;
154 }
155
156 std::conditional_t<true, std::condition_variable_any, std::condition_variable> cv;
157 std::mutex cv_mutex;
158 size_t mask_;
159 Slot<T>* slots_;
160 [[no_unique_address]] Allocator allocator_;
161
162 // Align to avoid false sharing between head_ and tail_
163 alignas(hardware_interference_size) std::atomic<size_t> head_{0};
164 alignas(hardware_interference_size) std::atomic<size_t> tail_{0};
165
166 static_assert(std::is_nothrow_copy_assignable_v<T> || std::is_nothrow_move_assignable_v<T>,
167 "T must be nothrow copy or move assignable");
168
169 static_assert(std::is_nothrow_destructible_v<T>, "T must be nothrow destructible");
170};
171} // namespace mpsc
172
173template <typename T, typename Allocator = mpsc::AlignedAllocator<mpsc::Slot<T>>>
174using MPSCQueue = mpsc::Queue<T, Allocator>;
175
176} // namespace Common
177
178#ifdef _MSC_VER
179#pragma warning(pop)
180#endif
diff --git a/src/common/elf.h b/src/common/elf.h
new file mode 100644
index 000000000..14a5e9597
--- /dev/null
+++ b/src/common/elf.h
@@ -0,0 +1,333 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <cstddef>
8
9#include "common_types.h"
10
11namespace Common {
12namespace ELF {
13
14/* Type for a 16-bit quantity. */
15using Elf32_Half = u16;
16using Elf64_Half = u16;
17
18/* Types for signed and unsigned 32-bit quantities. */
19using Elf32_Word = u32;
20using Elf32_Sword = s32;
21using Elf64_Word = u32;
22using Elf64_Sword = s32;
23
24/* Types for signed and unsigned 64-bit quantities. */
25using Elf32_Xword = u64;
26using Elf32_Sxword = s64;
27using Elf64_Xword = u64;
28using Elf64_Sxword = s64;
29
30/* Type of addresses. */
31using Elf32_Addr = u32;
32using Elf64_Addr = u64;
33
34/* Type of file offsets. */
35using Elf32_Off = u32;
36using Elf64_Off = u64;
37
38/* Type for section indices, which are 16-bit quantities. */
39using Elf32_Section = u16;
40using Elf64_Section = u16;
41
42/* Type for version symbol information. */
43using Elf32_Versym = Elf32_Half;
44using Elf64_Versym = Elf64_Half;
45
46constexpr size_t ElfIdentSize = 16;
47
48/* The ELF file header. This appears at the start of every ELF file. */
49
50struct Elf32_Ehdr {
51 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
52 Elf32_Half e_type; /* Object file type */
53 Elf32_Half e_machine; /* Architecture */
54 Elf32_Word e_version; /* Object file version */
55 Elf32_Addr e_entry; /* Entry point virtual address */
56 Elf32_Off e_phoff; /* Program header table file offset */
57 Elf32_Off e_shoff; /* Section header table file offset */
58 Elf32_Word e_flags; /* Processor-specific flags */
59 Elf32_Half e_ehsize; /* ELF header size in bytes */
60 Elf32_Half e_phentsize; /* Program header table entry size */
61 Elf32_Half e_phnum; /* Program header table entry count */
62 Elf32_Half e_shentsize; /* Section header table entry size */
63 Elf32_Half e_shnum; /* Section header table entry count */
64 Elf32_Half e_shstrndx; /* Section header string table index */
65};
66
67struct Elf64_Ehdr {
68 std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */
69 Elf64_Half e_type; /* Object file type */
70 Elf64_Half e_machine; /* Architecture */
71 Elf64_Word e_version; /* Object file version */
72 Elf64_Addr e_entry; /* Entry point virtual address */
73 Elf64_Off e_phoff; /* Program header table file offset */
74 Elf64_Off e_shoff; /* Section header table file offset */
75 Elf64_Word e_flags; /* Processor-specific flags */
76 Elf64_Half e_ehsize; /* ELF header size in bytes */
77 Elf64_Half e_phentsize; /* Program header table entry size */
78 Elf64_Half e_phnum; /* Program header table entry count */
79 Elf64_Half e_shentsize; /* Section header table entry size */
80 Elf64_Half e_shnum; /* Section header table entry count */
81 Elf64_Half e_shstrndx; /* Section header string table index */
82};
83
84constexpr u8 ElfClass32 = 1; /* 32-bit objects */
85constexpr u8 ElfClass64 = 2; /* 64-bit objects */
86constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */
87constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */
88constexpr u8 ElfOsAbiNone = 0; /* System V ABI */
89
90constexpr u16 ElfTypeNone = 0; /* No file type */
91constexpr u16 ElfTypeRel = 0; /* Relocatable file */
92constexpr u16 ElfTypeExec = 0; /* Executable file */
93constexpr u16 ElfTypeDyn = 0; /* Shared object file */
94
95constexpr u16 ElfMachineArm = 40; /* ARM */
96constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */
97
98constexpr std::array<u8, ElfIdentSize> Elf32Ident{
99 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
100
101constexpr std::array<u8, ElfIdentSize> Elf64Ident{
102 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone};
103
104/* Section header. */
105
106struct Elf32_Shdr {
107 Elf32_Word sh_name; /* Section name (string tbl index) */
108 Elf32_Word sh_type; /* Section type */
109 Elf32_Word sh_flags; /* Section flags */
110 Elf32_Addr sh_addr; /* Section virtual addr at execution */
111 Elf32_Off sh_offset; /* Section file offset */
112 Elf32_Word sh_size; /* Section size in bytes */
113 Elf32_Word sh_link; /* Link to another section */
114 Elf32_Word sh_info; /* Additional section information */
115 Elf32_Word sh_addralign; /* Section alignment */
116 Elf32_Word sh_entsize; /* Entry size if section holds table */
117};
118
119struct Elf64_Shdr {
120 Elf64_Word sh_name; /* Section name (string tbl index) */
121 Elf64_Word sh_type; /* Section type */
122 Elf64_Xword sh_flags; /* Section flags */
123 Elf64_Addr sh_addr; /* Section virtual addr at execution */
124 Elf64_Off sh_offset; /* Section file offset */
125 Elf64_Xword sh_size; /* Section size in bytes */
126 Elf64_Word sh_link; /* Link to another section */
127 Elf64_Word sh_info; /* Additional section information */
128 Elf64_Xword sh_addralign; /* Section alignment */
129 Elf64_Xword sh_entsize; /* Entry size if section holds table */
130};
131
132constexpr u32 ElfShnUndef = 0; /* Undefined section */
133
134constexpr u32 ElfShtNull = 0; /* Section header table entry unused */
135constexpr u32 ElfShtProgBits = 1; /* Program data */
136constexpr u32 ElfShtSymtab = 2; /* Symbol table */
137constexpr u32 ElfShtStrtab = 3; /* String table */
138constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */
139constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */
140constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */
141constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */
142constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */
143
144/* Symbol table entry. */
145
146struct Elf32_Sym {
147 Elf32_Word st_name; /* Symbol name (string tbl index) */
148 Elf32_Addr st_value; /* Symbol value */
149 Elf32_Word st_size; /* Symbol size */
150 u8 st_info; /* Symbol type and binding */
151 u8 st_other; /* Symbol visibility */
152 Elf32_Section st_shndx; /* Section index */
153};
154
155struct Elf64_Sym {
156 Elf64_Word st_name; /* Symbol name (string tbl index) */
157 u8 st_info; /* Symbol type and binding */
158 u8 st_other; /* Symbol visibility */
159 Elf64_Section st_shndx; /* Section index */
160 Elf64_Addr st_value; /* Symbol value */
161 Elf64_Xword st_size; /* Symbol size */
162};
163
164/* How to extract and insert information held in the st_info field. */
165
166static inline u8 ElfStBind(u8 st_info) {
167 return st_info >> 4;
168}
169static inline u8 ElfStType(u8 st_info) {
170 return st_info & 0xf;
171}
172static inline u8 ElfStInfo(u8 st_bind, u8 st_type) {
173 return static_cast<u8>((st_bind << 4) + (st_type & 0xf));
174}
175
176constexpr u8 ElfBindLocal = 0; /* Local symbol */
177constexpr u8 ElfBindGlobal = 1; /* Global symbol */
178constexpr u8 ElfBindWeak = 2; /* Weak symbol */
179
180constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */
181constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */
182constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */
183
184static inline u8 ElfStVisibility(u8 st_other) {
185 return static_cast<u8>(st_other & 0x3);
186}
187
188constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */
189constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */
190constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */
191constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */
192
193/* Relocation table entry without addend (in section of type ShtRel). */
194
195struct Elf32_Rel {
196 Elf32_Addr r_offset; /* Address */
197 Elf32_Word r_info; /* Relocation type and symbol index */
198};
199
200/* Relocation table entry with addend (in section of type ShtRela). */
201
202struct Elf32_Rela {
203 Elf32_Addr r_offset; /* Address */
204 Elf32_Word r_info; /* Relocation type and symbol index */
205 Elf32_Sword r_addend; /* Addend */
206};
207
208struct Elf64_Rela {
209 Elf64_Addr r_offset; /* Address */
210 Elf64_Xword r_info; /* Relocation type and symbol index */
211 Elf64_Sxword r_addend; /* Addend */
212};
213
214/* How to extract and insert information held in the r_info field. */
215
216static inline u32 Elf32RelSymIndex(Elf32_Word r_info) {
217 return r_info >> 8;
218}
219static inline u8 Elf32RelType(Elf32_Word r_info) {
220 return static_cast<u8>(r_info & 0xff);
221}
222static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) {
223 return (sym_index << 8) + type;
224}
225static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) {
226 return static_cast<u32>(r_info >> 32);
227}
228static inline u32 Elf64RelType(Elf64_Xword r_info) {
229 return r_info & 0xffffffff;
230}
231static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) {
232 return (static_cast<Elf64_Xword>(sym_index) << 32) + type;
233}
234
235constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */
236constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */
237constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */
238constexpr u32 ElfArmRelative = 23; /* Adjust by program base */
239
240constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */
241constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */
242constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */
243constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */
244
245/* Program segment header. */
246
247struct Elf32_Phdr {
248 Elf32_Word p_type; /* Segment type */
249 Elf32_Off p_offset; /* Segment file offset */
250 Elf32_Addr p_vaddr; /* Segment virtual address */
251 Elf32_Addr p_paddr; /* Segment physical address */
252 Elf32_Word p_filesz; /* Segment size in file */
253 Elf32_Word p_memsz; /* Segment size in memory */
254 Elf32_Word p_flags; /* Segment flags */
255 Elf32_Word p_align; /* Segment alignment */
256};
257
258struct Elf64_Phdr {
259 Elf64_Word p_type; /* Segment type */
260 Elf64_Word p_flags; /* Segment flags */
261 Elf64_Off p_offset; /* Segment file offset */
262 Elf64_Addr p_vaddr; /* Segment virtual address */
263 Elf64_Addr p_paddr; /* Segment physical address */
264 Elf64_Xword p_filesz; /* Segment size in file */
265 Elf64_Xword p_memsz; /* Segment size in memory */
266 Elf64_Xword p_align; /* Segment alignment */
267};
268
269/* Legal values for p_type (segment type). */
270
271constexpr u32 ElfPtNull = 0; /* Program header table entry unused */
272constexpr u32 ElfPtLoad = 1; /* Loadable program segment */
273constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */
274constexpr u32 ElfPtInterp = 3; /* Program interpreter */
275constexpr u32 ElfPtNote = 4; /* Auxiliary information */
276constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */
277constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */
278
279/* Legal values for p_flags (segment flags). */
280
281constexpr u32 ElfPfExec = 0; /* Segment is executable */
282constexpr u32 ElfPfWrite = 1; /* Segment is writable */
283constexpr u32 ElfPfRead = 2; /* Segment is readable */
284
285/* Dynamic section entry. */
286
287struct Elf32_Dyn {
288 Elf32_Sword d_tag; /* Dynamic entry type */
289 union {
290 Elf32_Word d_val; /* Integer value */
291 Elf32_Addr d_ptr; /* Address value */
292 } d_un;
293};
294
295struct Elf64_Dyn {
296 Elf64_Sxword d_tag; /* Dynamic entry type */
297 union {
298 Elf64_Xword d_val; /* Integer value */
299 Elf64_Addr d_ptr; /* Address value */
300 } d_un;
301};
302
303/* Legal values for d_tag (dynamic entry type). */
304
305constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */
306constexpr u32 ElfDtNeeded = 1; /* Name of needed library */
307constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */
308constexpr u32 ElfDtPltGot = 3; /* Processor defined value */
309constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */
310constexpr u32 ElfDtStrtab = 5; /* Address of string table */
311constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */
312constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */
313constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */
314constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */
315constexpr u32 ElfDtStrsz = 10; /* Size of string table */
316constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */
317constexpr u32 ElfDtInit = 12; /* Address of init function */
318constexpr u32 ElfDtFini = 13; /* Address of termination function */
319constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */
320constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */
321constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */
322constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */
323constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */
324constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */
325constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */
326constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */
327constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */
328constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */
329constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */
330constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */
331
332} // namespace ELF
333} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 9a9c74a70..6ffab63af 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -70,6 +70,7 @@ void LogSettings() {
70 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); 70 log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir));
71 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); 71 log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir));
72 log_setting("Debugging_ProgramArgs", values.program_args.GetValue()); 72 log_setting("Debugging_ProgramArgs", values.program_args.GetValue());
73 log_setting("Debugging_GDBStub", values.use_gdbstub.GetValue());
73 log_setting("Input_EnableMotion", values.motion_enabled.GetValue()); 74 log_setting("Input_EnableMotion", values.motion_enabled.GetValue());
74 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue()); 75 log_setting("Input_EnableVibration", values.vibration_enabled.GetValue());
75 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue()); 76 log_setting("Input_EnableRawInput", values.enable_raw_input.GetValue());
diff --git a/src/common/settings.h b/src/common/settings.h
index e61d9cd7f..a507744a2 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -496,7 +496,7 @@ struct Values {
496 496
497 // Renderer 497 // Renderer
498 RangedSetting<RendererBackend> renderer_backend{ 498 RangedSetting<RendererBackend> renderer_backend{
499 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 499 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
500 BasicSetting<bool> renderer_debug{false, "debug"}; 500 BasicSetting<bool> renderer_debug{false, "debug"};
501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
@@ -601,7 +601,7 @@ struct Values {
601 // Debugging 601 // Debugging
602 bool record_frame_times; 602 bool record_frame_times;
603 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"}; 603 BasicSetting<bool> use_gdbstub{false, "use_gdbstub"};
604 BasicSetting<u16> gdbstub_port{0, "gdbstub_port"}; 604 BasicSetting<u16> gdbstub_port{6543, "gdbstub_port"};
605 BasicSetting<std::string> program_args{std::string(), "program_args"}; 605 BasicSetting<std::string> program_args{std::string(), "program_args"};
606 BasicSetting<bool> dump_exefs{false, "dump_exefs"}; 606 BasicSetting<bool> dump_exefs{false, "dump_exefs"};
607 BasicSetting<bool> dump_nso{false, "dump_nso"}; 607 BasicSetting<bool> dump_nso{false, "dump_nso"};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 62230bae0..2bd720f08 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -36,6 +36,13 @@ add_library(core STATIC
36 crypto/ctr_encryption_layer.h 36 crypto/ctr_encryption_layer.h
37 crypto/xts_encryption_layer.cpp 37 crypto/xts_encryption_layer.cpp
38 crypto/xts_encryption_layer.h 38 crypto/xts_encryption_layer.h
39 debugger/debugger_interface.h
40 debugger/debugger.cpp
41 debugger/debugger.h
42 debugger/gdbstub_arch.cpp
43 debugger/gdbstub_arch.h
44 debugger/gdbstub.cpp
45 debugger/gdbstub.h
39 device_memory.cpp 46 device_memory.cpp
40 device_memory.h 47 device_memory.h
41 file_sys/bis_factory.cpp 48 file_sys/bis_factory.cpp
@@ -761,6 +768,9 @@ create_target_directory_groups(core)
761 768
762target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 769target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
763target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus) 770target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
771if (MINGW)
772 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
773endif()
764 774
765if (ENABLE_WEB_SERVICE) 775if (ENABLE_WEB_SERVICE)
766 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 776 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..9b5a5ca57 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -9,7 +9,9 @@
9#include "core/arm/arm_interface.h" 9#include "core/arm/arm_interface.h"
10#include "core/arm/symbols.h" 10#include "core/arm/symbols.h"
11#include "core/core.h" 11#include "core/core.h"
12#include "core/debugger/debugger.h"
12#include "core/hle/kernel/k_process.h" 13#include "core/hle/kernel/k_process.h"
14#include "core/hle/kernel/svc.h"
13#include "core/loader/loader.h" 15#include "core/loader/loader.h"
14#include "core/memory.h" 16#include "core/memory.h"
15 17
@@ -88,4 +90,48 @@ void ARM_Interface::LogBacktrace() const {
88 } 90 }
89} 91}
90 92
93void ARM_Interface::Run() {
94 using Kernel::StepState;
95 using Kernel::SuspendType;
96
97 while (true) {
98 Kernel::KThread* current_thread{system.Kernel().CurrentScheduler()->GetCurrentThread()};
99 Dynarmic::HaltReason hr{};
100
101 // Notify the debugger and go to sleep if a step was performed
102 // and this thread has been scheduled again.
103 if (current_thread->GetStepState() == StepState::StepPerformed) {
104 system.GetDebugger().NotifyThreadStopped(current_thread);
105 current_thread->RequestSuspend(SuspendType::Debug);
106 break;
107 }
108
109 // Otherwise, run the thread.
110 if (current_thread->GetStepState() == StepState::StepPending) {
111 hr = StepJit();
112
113 if (Has(hr, step_thread)) {
114 current_thread->SetStepState(StepState::StepPerformed);
115 }
116 } else {
117 hr = RunJit();
118 }
119
120 // Notify the debugger and go to sleep if a breakpoint was hit.
121 if (Has(hr, breakpoint)) {
122 system.GetDebugger().NotifyThreadStopped(current_thread);
123 current_thread->RequestSuspend(Kernel::SuspendType::Debug);
124 break;
125 }
126
127 // Handle syscalls and scheduling (this may change the current thread)
128 if (Has(hr, svc_call)) {
129 Kernel::Svc::Call(system, GetSvcNumber());
130 }
131 if (Has(hr, break_loop) || !uses_wall_clock) {
132 break;
133 }
134 }
135}
136
91} // namespace Core 137} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8ce973a77..66f6107e9 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -6,6 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
10#include <dynarmic/interface/halt_reason.h>
11
9#include "common/common_funcs.h" 12#include "common/common_funcs.h"
10#include "common/common_types.h" 13#include "common/common_types.h"
11#include "core/hardware_properties.h" 14#include "core/hardware_properties.h"
@@ -64,10 +67,7 @@ public:
64 static_assert(sizeof(ThreadContext64) == 0x320); 67 static_assert(sizeof(ThreadContext64) == 0x320);
65 68
66 /// Runs the CPU until an event happens 69 /// Runs the CPU until an event happens
67 virtual void Run() = 0; 70 void Run();
68
69 /// Step CPU by one instruction
70 virtual void Step() = 0;
71 71
72 /// Clear all instruction cache 72 /// Clear all instruction cache
73 virtual void ClearInstructionCache() = 0; 73 virtual void ClearInstructionCache() = 0;
@@ -194,6 +194,11 @@ public:
194 194
195 void LogBacktrace() const; 195 void LogBacktrace() const;
196 196
197 static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
198 static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
199 static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
200 static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
201
197protected: 202protected:
198 /// System context that this ARM interface is running under. 203 /// System context that this ARM interface is running under.
199 System& system; 204 System& system;
@@ -201,6 +206,10 @@ protected:
201 bool uses_wall_clock; 206 bool uses_wall_clock;
202 207
203 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out); 208 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
209
210 virtual Dynarmic::HaltReason RunJit() = 0;
211 virtual Dynarmic::HaltReason StepJit() = 0;
212 virtual u32 GetSvcNumber() const = 0;
204}; 213};
205 214
206} // namespace Core 215} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 781a77f6f..7c82d0b96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -17,6 +17,8 @@
17#include "core/arm/dynarmic/arm_exclusive_monitor.h" 17#include "core/arm/dynarmic/arm_exclusive_monitor.h"
18#include "core/core.h" 18#include "core/core.h"
19#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/debugger/debugger.h"
21#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/svc.h" 22#include "core/hle/kernel/svc.h"
21#include "core/memory.h" 23#include "core/memory.h"
22 24
@@ -24,9 +26,6 @@ namespace Core {
24 26
25using namespace Common::Literals; 27using namespace Common::Literals;
26 28
27constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
28constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
29
30class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 29class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
31public: 30public:
32 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 31 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -78,16 +77,21 @@ public:
78 } 77 }
79 78
80 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 79 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
80 if (parent.system.DebuggerEnabled()) {
81 parent.jit.load()->Regs()[15] = pc;
82 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
83 return;
84 }
85
81 parent.LogBacktrace(); 86 parent.LogBacktrace();
82 LOG_CRITICAL(Core_ARM, 87 LOG_CRITICAL(Core_ARM,
83 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 88 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
84 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 89 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
85 UNIMPLEMENTED();
86 } 90 }
87 91
88 void CallSVC(u32 swi) override { 92 void CallSVC(u32 swi) override {
89 parent.svc_swi = swi; 93 parent.svc_swi = swi;
90 parent.jit.load()->HaltExecution(svc_call); 94 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
91 } 95 }
92 96
93 void AddTicks(u64 ticks) override { 97 void AddTicks(u64 ticks) override {
@@ -232,20 +236,16 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
232 return std::make_unique<Dynarmic::A32::Jit>(config); 236 return std::make_unique<Dynarmic::A32::Jit>(config);
233} 237}
234 238
235void ARM_Dynarmic_32::Run() { 239Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
236 while (true) { 240 return jit.load()->Run();
237 const auto hr = jit.load()->Run(); 241}
238 if (Has(hr, svc_call)) { 242
239 Kernel::Svc::Call(system, svc_swi); 243Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
240 } 244 return jit.load()->Step();
241 if (Has(hr, break_loop) || !uses_wall_clock) {
242 break;
243 }
244 }
245} 245}
246 246
247void ARM_Dynarmic_32::Step() { 247u32 ARM_Dynarmic_32::GetSvcNumber() const {
248 jit.load()->Step(); 248 return svc_swi;
249} 249}
250 250
251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 251ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index abfe76644..5b1d60005 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -41,8 +41,6 @@ public:
41 void SetVectorReg(int index, u128 value) override; 41 void SetVectorReg(int index, u128 value) override;
42 u32 GetPSTATE() const override; 42 u32 GetPSTATE() const override;
43 void SetPSTATE(u32 pstate) override; 43 void SetPSTATE(u32 pstate) override;
44 void Run() override;
45 void Step() override;
46 VAddr GetTlsAddress() const override; 44 VAddr GetTlsAddress() const override;
47 void SetTlsAddress(VAddr address) override; 45 void SetTlsAddress(VAddr address) override;
48 void SetTPIDR_EL0(u64 value) override; 46 void SetTPIDR_EL0(u64 value) override;
@@ -70,6 +68,11 @@ public:
70 68
71 std::vector<BacktraceEntry> GetBacktrace() const override; 69 std::vector<BacktraceEntry> GetBacktrace() const override;
72 70
71protected:
72 Dynarmic::HaltReason RunJit() override;
73 Dynarmic::HaltReason StepJit() override;
74 u32 GetSvcNumber() const override;
75
73private: 76private:
74 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 77 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
75 78
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1b1334598..d4c67eafd 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -15,6 +15,7 @@
15#include "core/arm/dynarmic/arm_exclusive_monitor.h" 15#include "core/arm/dynarmic/arm_exclusive_monitor.h"
16#include "core/core.h" 16#include "core/core.h"
17#include "core/core_timing.h" 17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
18#include "core/hardware_properties.h" 19#include "core/hardware_properties.h"
19#include "core/hle/kernel/k_process.h" 20#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/svc.h" 21#include "core/hle/kernel/svc.h"
@@ -25,9 +26,6 @@ namespace Core {
25using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
26using namespace Common::Literals; 27using namespace Common::Literals;
27 28
28constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
29constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
30
31class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
32public: 30public:
33 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -119,8 +117,13 @@ public:
119 case Dynarmic::A64::Exception::SendEventLocal: 117 case Dynarmic::A64::Exception::SendEventLocal:
120 case Dynarmic::A64::Exception::Yield: 118 case Dynarmic::A64::Exception::Yield:
121 return; 119 return;
122 case Dynarmic::A64::Exception::Breakpoint:
123 default: 120 default:
121 if (parent.system.DebuggerEnabled()) {
122 parent.jit.load()->SetPC(pc);
123 parent.jit.load()->HaltExecution(ARM_Interface::breakpoint);
124 return;
125 }
126
124 parent.LogBacktrace(); 127 parent.LogBacktrace();
125 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 128 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
126 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 129 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
@@ -129,7 +132,7 @@ public:
129 132
130 void CallSVC(u32 swi) override { 133 void CallSVC(u32 swi) override {
131 parent.svc_swi = swi; 134 parent.svc_swi = swi;
132 parent.jit.load()->HaltExecution(svc_call); 135 parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
133 } 136 }
134 137
135 void AddTicks(u64 ticks) override { 138 void AddTicks(u64 ticks) override {
@@ -293,20 +296,16 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
293 return std::make_shared<Dynarmic::A64::Jit>(config); 296 return std::make_shared<Dynarmic::A64::Jit>(config);
294} 297}
295 298
296void ARM_Dynarmic_64::Run() { 299Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
297 while (true) { 300 return jit.load()->Run();
298 const auto hr = jit.load()->Run(); 301}
299 if (Has(hr, svc_call)) { 302
300 Kernel::Svc::Call(system, svc_swi); 303Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
301 } 304 return jit.load()->Step();
302 if (Has(hr, break_loop) || !uses_wall_clock) {
303 break;
304 }
305 }
306} 305}
307 306
308void ARM_Dynarmic_64::Step() { 307u32 ARM_Dynarmic_64::GetSvcNumber() const {
309 jit.load()->Step(); 308 return svc_swi;
310} 309}
311 310
312ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 311ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 01a7e4dad..abfbc3c3f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -39,8 +39,6 @@ public:
39 void SetVectorReg(int index, u128 value) override; 39 void SetVectorReg(int index, u128 value) override;
40 u32 GetPSTATE() const override; 40 u32 GetPSTATE() const override;
41 void SetPSTATE(u32 pstate) override; 41 void SetPSTATE(u32 pstate) override;
42 void Run() override;
43 void Step() override;
44 VAddr GetTlsAddress() const override; 42 VAddr GetTlsAddress() const override;
45 void SetTlsAddress(VAddr address) override; 43 void SetTlsAddress(VAddr address) override;
46 void SetTPIDR_EL0(u64 value) override; 44 void SetTPIDR_EL0(u64 value) override;
@@ -64,6 +62,11 @@ public:
64 62
65 std::vector<BacktraceEntry> GetBacktrace() const override; 63 std::vector<BacktraceEntry> GetBacktrace() const override;
66 64
65protected:
66 Dynarmic::HaltReason RunJit() override;
67 Dynarmic::HaltReason StepJit() override;
68 u32 GetSvcNumber() const override;
69
67private: 70private:
68 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 71 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
69 std::size_t address_space_bits) const; 72 std::size_t address_space_bits) const;
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
index 4aa1a1ee1..0259c7ea2 100644
--- a/src/core/arm/symbols.cpp
+++ b/src/core/arm/symbols.cpp
@@ -3,73 +3,14 @@
3 3
4#include "common/bit_field.h" 4#include "common/bit_field.h"
5#include "common/common_funcs.h" 5#include "common/common_funcs.h"
6#include "common/elf.h"
6#include "core/arm/symbols.h" 7#include "core/arm/symbols.h"
7#include "core/core.h" 8#include "core/core.h"
8#include "core/memory.h" 9#include "core/memory.h"
9 10
10namespace Core { 11using namespace Common::ELF;
11namespace {
12
13constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
14constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
15constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
16constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
17
18enum class ELFSymbolType : u8 {
19 None = 0,
20 Object = 1,
21 Function = 2,
22 Section = 3,
23 File = 4,
24 Common = 5,
25 TLS = 6,
26};
27
28enum class ELFSymbolBinding : u8 {
29 Local = 0,
30 Global = 1,
31 Weak = 2,
32};
33
34enum class ELFSymbolVisibility : u8 {
35 Default = 0,
36 Internal = 1,
37 Hidden = 2,
38 Protected = 3,
39};
40
41struct ELF64Symbol {
42 u32 name_index;
43 union {
44 u8 info;
45
46 BitField<0, 4, ELFSymbolType> type;
47 BitField<4, 4, ELFSymbolBinding> binding;
48 };
49 ELFSymbolVisibility visibility;
50 u16 sh_index;
51 u64 value;
52 u64 size;
53};
54static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
55
56struct ELF32Symbol {
57 u32 name_index;
58 u32 value;
59 u32 size;
60 union {
61 u8 info;
62
63 BitField<0, 4, ELFSymbolType> type;
64 BitField<4, 4, ELFSymbolBinding> binding;
65 };
66 ELFSymbolVisibility visibility;
67 u16 sh_index;
68};
69static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
70
71} // Anonymous namespace
72 12
13namespace Core {
73namespace Symbols { 14namespace Symbols {
74 15
75template <typename Word, typename ELFSymbol, typename ByteReader> 16template <typename Word, typename ELFSymbol, typename ByteReader>
@@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
110 const Word value = ReadWord(dynamic_index + sizeof(Word)); 51 const Word value = ReadWord(dynamic_index + sizeof(Word));
111 dynamic_index += 2 * sizeof(Word); 52 dynamic_index += 2 * sizeof(Word);
112 53
113 if (tag == ELF_DYNAMIC_TAG_NULL) { 54 if (tag == ElfDtNull) {
114 break; 55 break;
115 } 56 }
116 57
117 if (tag == ELF_DYNAMIC_TAG_STRTAB) { 58 if (tag == ElfDtStrtab) {
118 string_table_offset = value; 59 string_table_offset = value;
119 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { 60 } else if (tag == ElfDtSymtab) {
120 symbol_table_offset = value; 61 symbol_table_offset = value;
121 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { 62 } else if (tag == ElfDtSyment) {
122 symbol_entry_size = value; 63 symbol_entry_size = value;
123 } 64 }
124 } 65 }
@@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) {
134 ELFSymbol symbol{}; 75 ELFSymbol symbol{};
135 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); 76 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
136 77
137 VAddr string_offset = string_table_offset + symbol.name_index; 78 VAddr string_offset = string_table_offset + symbol.st_name;
138 std::string name; 79 std::string name;
139 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { 80 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
140 name += static_cast<char>(c); 81 name += static_cast<char>(c);
141 } 82 }
142 83
143 symbol_index += symbol_entry_size; 84 symbol_index += symbol_entry_size;
144 out[name] = std::make_pair(symbol.value, symbol.size); 85 out[name] = std::make_pair(symbol.st_value, symbol.st_size);
145 } 86 }
146 87
147 return out; 88 return out;
@@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; 93 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
153 94
154 if (is_64) { 95 if (is_64) {
155 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 96 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
156 } else { 97 } else {
157 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 98 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
158 } 99 }
159} 100}
160 101
@@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) {
164 }}; 105 }};
165 106
166 if (is_64) { 107 if (is_64) {
167 return GetSymbols<u64, ELF64Symbol>(ReadBytes); 108 return GetSymbols<u64, Elf64_Sym>(ReadBytes);
168 } else { 109 } else {
169 return GetSymbols<u32, ELF32Symbol>(ReadBytes); 110 return GetSymbols<u32, Elf32_Sym>(ReadBytes);
170 } 111 }
171} 112}
172 113
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8a887904d..7d974ba65 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
17#include "core/core.h" 17#include "core/core.h"
18#include "core/core_timing.h" 18#include "core/core_timing.h"
19#include "core/cpu_manager.h" 19#include "core/cpu_manager.h"
20#include "core/debugger/debugger.h"
20#include "core/device_memory.h" 21#include "core/device_memory.h"
21#include "core/file_sys/bis_factory.h" 22#include "core/file_sys/bis_factory.h"
22#include "core/file_sys/mode.h" 23#include "core/file_sys/mode.h"
@@ -171,6 +172,10 @@ struct System::Impl {
171 } 172 }
172 } 173 }
173 174
175 void InitializeDebugger(System& system, u16 port) {
176 debugger = std::make_unique<Debugger>(system, port);
177 }
178
174 SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 179 SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
175 LOG_DEBUG(Core, "initialized OK"); 180 LOG_DEBUG(Core, "initialized OK");
176 181
@@ -329,6 +334,7 @@ struct System::Impl {
329 gpu_core->NotifyShutdown(); 334 gpu_core->NotifyShutdown();
330 } 335 }
331 336
337 debugger.reset();
332 services.reset(); 338 services.reset();
333 service_manager.reset(); 339 service_manager.reset();
334 cheat_engine.reset(); 340 cheat_engine.reset();
@@ -436,6 +442,9 @@ struct System::Impl {
436 /// Network instance 442 /// Network instance
437 Network::NetworkInstance network_instance; 443 Network::NetworkInstance network_instance;
438 444
445 /// Debugger
446 std::unique_ptr<Core::Debugger> debugger;
447
439 SystemResultStatus status = SystemResultStatus::Success; 448 SystemResultStatus status = SystemResultStatus::Success;
440 std::string status_details = ""; 449 std::string status_details = "";
441 450
@@ -472,10 +481,6 @@ SystemResultStatus System::Pause() {
472 return impl->Pause(); 481 return impl->Pause();
473} 482}
474 483
475SystemResultStatus System::SingleStep() {
476 return SystemResultStatus::Success;
477}
478
479void System::InvalidateCpuInstructionCaches() { 484void System::InvalidateCpuInstructionCaches() {
480 impl->kernel.InvalidateAllInstructionCaches(); 485 impl->kernel.InvalidateAllInstructionCaches();
481} 486}
@@ -496,6 +501,10 @@ void System::UnstallCPU() {
496 impl->UnstallCPU(); 501 impl->UnstallCPU();
497} 502}
498 503
504void System::InitializeDebugger() {
505 impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
506}
507
499SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 508SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
500 u64 program_id, std::size_t program_index) { 509 u64 program_id, std::size_t program_index) {
501 return impl->Load(*this, emu_window, filepath, program_id, program_index); 510 return impl->Load(*this, emu_window, filepath, program_id, program_index);
@@ -809,6 +818,18 @@ bool System::IsMulticore() const {
809 return impl->is_multicore; 818 return impl->is_multicore;
810} 819}
811 820
821bool System::DebuggerEnabled() const {
822 return Settings::values.use_gdbstub.GetValue();
823}
824
825Core::Debugger& System::GetDebugger() {
826 return *impl->debugger;
827}
828
829const Core::Debugger& System::GetDebugger() const {
830 return *impl->debugger;
831}
832
812void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) { 833void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
813 impl->execute_program_callback = std::move(callback); 834 impl->execute_program_callback = std::move(callback);
814} 835}
diff --git a/src/core/core.h b/src/core/core.h
index 4a0c7dc84..94477206e 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -97,6 +97,7 @@ namespace Core {
97 97
98class ARM_Interface; 98class ARM_Interface;
99class CpuManager; 99class CpuManager;
100class Debugger;
100class DeviceMemory; 101class DeviceMemory;
101class ExclusiveMonitor; 102class ExclusiveMonitor;
102class SpeedLimiter; 103class SpeedLimiter;
@@ -148,12 +149,6 @@ public:
148 [[nodiscard]] SystemResultStatus Pause(); 149 [[nodiscard]] SystemResultStatus Pause();
149 150
150 /** 151 /**
151 * Step the CPU one instruction
152 * @return Result status, indicating whether or not the operation succeeded.
153 */
154 [[nodiscard]] SystemResultStatus SingleStep();
155
156 /**
157 * Invalidate the CPU instruction caches 152 * Invalidate the CPU instruction caches
158 * This function should only be used by GDB Stub to support breakpoints, memory updates and 153 * This function should only be used by GDB Stub to support breakpoints, memory updates and
159 * step/continue commands. 154 * step/continue commands.
@@ -169,6 +164,11 @@ public:
169 void UnstallCPU(); 164 void UnstallCPU();
170 165
171 /** 166 /**
167 * Initialize the debugger.
168 */
169 void InitializeDebugger();
170
171 /**
172 * Load an executable application. 172 * Load an executable application.
173 * @param emu_window Reference to the host-system window used for video output and keyboard 173 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 174 * input.
@@ -354,6 +354,9 @@ public:
354 [[nodiscard]] Service::Time::TimeManager& GetTimeManager(); 354 [[nodiscard]] Service::Time::TimeManager& GetTimeManager();
355 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const; 355 [[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
356 356
357 [[nodiscard]] Core::Debugger& GetDebugger();
358 [[nodiscard]] const Core::Debugger& GetDebugger() const;
359
357 void SetExitLock(bool locked); 360 void SetExitLock(bool locked);
358 [[nodiscard]] bool GetExitLock() const; 361 [[nodiscard]] bool GetExitLock() const;
359 362
@@ -375,6 +378,9 @@ public:
375 /// Tells if system is running on multicore. 378 /// Tells if system is running on multicore.
376 [[nodiscard]] bool IsMulticore() const; 379 [[nodiscard]] bool IsMulticore() const;
377 380
381 /// Tells if the system debugger is enabled.
382 [[nodiscard]] bool DebuggerEnabled() const;
383
378 /// Type used for the frontend to designate a callback for System to re-launch the application 384 /// Type used for the frontend to designate a callback for System to re-launch the application
379 /// using a specified program index. 385 /// using a specified program index.
380 using ExecuteProgramCallback = std::function<void(std::size_t)>; 386 using ExecuteProgramCallback = std::function<void(std::size_t)>;
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
new file mode 100644
index 000000000..8d64990ed
--- /dev/null
+++ b/src/core/debugger/debugger.cpp
@@ -0,0 +1,269 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <mutex>
6#include <thread>
7
8#include <boost/asio.hpp>
9#include <boost/process/async_pipe.hpp>
10
11#include "common/logging/log.h"
12#include "common/thread.h"
13#include "core/core.h"
14#include "core/debugger/debugger.h"
15#include "core/debugger/debugger_interface.h"
16#include "core/debugger/gdbstub.h"
17#include "core/hle/kernel/global_scheduler_context.h"
18
19template <typename Readable, typename Buffer, typename Callback>
20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
21 static_assert(std::is_trivial_v<Buffer>);
22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
23 r.async_read_some(
24 boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
25 if (!error.failed()) {
26 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
27 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
28 c(received_data);
29 }
30
31 AsyncReceiveInto(r, buffer, c);
32 });
33}
34
35template <typename Readable, typename Buffer>
36static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
37 static_assert(std::is_trivial_v<Buffer>);
38 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
39 size_t bytes_read = r.read_some(boost_buffer);
40 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
41 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
42 return received_data;
43}
44
45namespace Core {
46
47class DebuggerImpl : public DebuggerBackend {
48public:
49 explicit DebuggerImpl(Core::System& system_, u16 port)
50 : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
51 frontend = std::make_unique<GDBStub>(*this, system);
52 InitializeServer(port);
53 }
54
55 ~DebuggerImpl() override {
56 ShutdownServer();
57 }
58
59 bool NotifyThreadStopped(Kernel::KThread* thread) {
60 std::scoped_lock lk{connection_lock};
61
62 if (stopped) {
63 // Do not notify the debugger about another event.
64 // It should be ignored.
65 return false;
66 }
67 stopped = true;
68
69 boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
70 return true;
71 }
72
73 std::span<const u8> ReadFromClient() override {
74 return ReceiveInto(client_socket, client_data);
75 }
76
77 void WriteToClient(std::span<const u8> data) override {
78 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
79 }
80
81 void SetActiveThread(Kernel::KThread* thread) override {
82 active_thread = thread;
83 }
84
85 Kernel::KThread* GetActiveThread() override {
86 return active_thread;
87 }
88
89private:
90 void InitializeServer(u16 port) {
91 using boost::asio::ip::tcp;
92
93 LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
94
95 // Run the connection thread.
96 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
97 try {
98 // Initialize the listening socket and accept a new client.
99 tcp::endpoint endpoint{boost::asio::ip::address_v4::loopback(), port};
100 tcp::acceptor acceptor{io_context, endpoint};
101
102 acceptor.async_accept(client_socket, [](const auto&) {});
103 io_context.run_one();
104 io_context.restart();
105
106 if (stop_token.stop_requested()) {
107 return;
108 }
109
110 ThreadLoop(stop_token);
111 } catch (const std::exception& ex) {
112 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
113 }
114 });
115 }
116
117 void ShutdownServer() {
118 connection_thread.request_stop();
119 io_context.stop();
120 connection_thread.join();
121 }
122
123 void ThreadLoop(std::stop_token stop_token) {
124 Common::SetCurrentThreadName("yuzu:Debugger");
125
126 // Set up the client signals for new data.
127 AsyncReceiveInto(signal_pipe, active_thread, [&](auto d) { PipeData(d); });
128 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
129
130 // Stop the emulated CPU.
131 AllCoreStop();
132
133 // Set the active thread.
134 UpdateActiveThread();
135
136 // Set up the frontend.
137 frontend->Connected();
138
139 // Main event loop.
140 while (!stop_token.stop_requested() && io_context.run()) {
141 }
142 }
143
144 void PipeData(std::span<const u8> data) {
145 AllCoreStop();
146 UpdateActiveThread();
147 frontend->Stopped(active_thread);
148 }
149
150 void ClientData(std::span<const u8> data) {
151 const auto actions{frontend->ClientData(data)};
152 for (const auto action : actions) {
153 switch (action) {
154 case DebuggerAction::Interrupt: {
155 {
156 std::scoped_lock lk{connection_lock};
157 stopped = true;
158 }
159 AllCoreStop();
160 UpdateActiveThread();
161 frontend->Stopped(active_thread);
162 break;
163 }
164 case DebuggerAction::Continue:
165 active_thread->SetStepState(Kernel::StepState::NotStepping);
166 ResumeInactiveThreads();
167 AllCoreResume();
168 break;
169 case DebuggerAction::StepThreadUnlocked:
170 active_thread->SetStepState(Kernel::StepState::StepPending);
171 ResumeInactiveThreads();
172 AllCoreResume();
173 break;
174 case DebuggerAction::StepThreadLocked:
175 active_thread->SetStepState(Kernel::StepState::StepPending);
176 SuspendInactiveThreads();
177 AllCoreResume();
178 break;
179 case DebuggerAction::ShutdownEmulation: {
180 // Suspend all threads and release any locks held
181 active_thread->RequestSuspend(Kernel::SuspendType::Debug);
182 SuspendInactiveThreads();
183 AllCoreResume();
184
185 // Spawn another thread that will exit after shutdown,
186 // to avoid a deadlock
187 Core::System* system_ref{&system};
188 std::thread t([system_ref] { system_ref->Exit(); });
189 t.detach();
190 break;
191 }
192 }
193 }
194 }
195
196 void AllCoreStop() {
197 if (!suspend) {
198 suspend = system.StallCPU();
199 }
200 }
201
202 void AllCoreResume() {
203 stopped = false;
204 system.UnstallCPU();
205 suspend.reset();
206 }
207
208 void SuspendInactiveThreads() {
209 for (auto* thread : ThreadList()) {
210 if (thread != active_thread) {
211 thread->RequestSuspend(Kernel::SuspendType::Debug);
212 }
213 }
214 }
215
216 void ResumeInactiveThreads() {
217 for (auto* thread : ThreadList()) {
218 if (thread != active_thread) {
219 thread->Resume(Kernel::SuspendType::Debug);
220 thread->SetStepState(Kernel::StepState::NotStepping);
221 }
222 }
223 }
224
225 void UpdateActiveThread() {
226 const auto& threads{ThreadList()};
227 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
228 active_thread = threads[0];
229 }
230 active_thread->Resume(Kernel::SuspendType::Debug);
231 active_thread->SetStepState(Kernel::StepState::NotStepping);
232 }
233
234 const std::vector<Kernel::KThread*>& ThreadList() {
235 return system.GlobalSchedulerContext().GetThreadList();
236 }
237
238private:
239 System& system;
240 std::unique_ptr<DebuggerFrontend> frontend;
241
242 std::jthread connection_thread;
243 std::mutex connection_lock;
244 boost::asio::io_context io_context;
245 boost::process::async_pipe signal_pipe;
246 boost::asio::ip::tcp::socket client_socket;
247 std::optional<std::unique_lock<std::mutex>> suspend;
248
249 Kernel::KThread* active_thread;
250 bool stopped;
251
252 std::array<u8, 4096> client_data;
253};
254
255Debugger::Debugger(Core::System& system, u16 port) {
256 try {
257 impl = std::make_unique<DebuggerImpl>(system, port);
258 } catch (const std::exception& ex) {
259 LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
260 }
261}
262
263Debugger::~Debugger() = default;
264
265bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
266 return impl && impl->NotifyThreadStopped(thread);
267}
268
269} // namespace Core
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
new file mode 100644
index 000000000..ea36c6ab2
--- /dev/null
+++ b/src/core/debugger/debugger.h
@@ -0,0 +1,41 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <memory>
7
8#include "common/common_types.h"
9
10namespace Kernel {
11class KThread;
12}
13
14namespace Core {
15class System;
16
17class DebuggerImpl;
18
19class Debugger {
20public:
21 /**
22 * Blocks and waits for a connection on localhost, port `server_port`.
23 * Does not create the debugger if the port is already in use.
24 */
25 explicit Debugger(Core::System& system, u16 server_port);
26 ~Debugger();
27
28 /**
29 * Notify the debugger that the given thread is stopped
30 * (due to a breakpoint, or due to stopping after a successful step).
31 *
32 * The debugger will asynchronously halt emulation after the notification has
33 * occurred. If another thread attempts to notify before emulation has stopped,
34 * it is ignored and this method will return false. Otherwise it will return true.
35 */
36 bool NotifyThreadStopped(Kernel::KThread* thread);
37
38private:
39 std::unique_ptr<DebuggerImpl> impl;
40};
41} // namespace Core
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
new file mode 100644
index 000000000..35ba0bc61
--- /dev/null
+++ b/src/core/debugger/debugger_interface.h
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include <span>
8#include <vector>
9
10#include "common/common_types.h"
11
12namespace Kernel {
13class KThread;
14}
15
16namespace Core {
17
18enum class DebuggerAction {
19 Interrupt, ///< Stop emulation as soon as possible.
20 Continue, ///< Resume emulation.
21 StepThreadLocked, ///< Step the currently-active thread without resuming others.
22 StepThreadUnlocked, ///< Step the currently-active thread and resume others.
23 ShutdownEmulation, ///< Shut down the emulator.
24};
25
26class DebuggerBackend {
27public:
28 virtual ~DebuggerBackend() = default;
29
30 /**
31 * Can be invoked from a callback to synchronously wait for more data.
32 * Will return as soon as least one byte is received. Reads up to 4096 bytes.
33 */
34 virtual std::span<const u8> ReadFromClient() = 0;
35
36 /**
37 * Can be invoked from a callback to write data to the client.
38 * Returns immediately after the data is sent.
39 */
40 virtual void WriteToClient(std::span<const u8> data) = 0;
41
42 /**
43 * Gets the currently active thread when the debugger is stopped.
44 */
45 virtual Kernel::KThread* GetActiveThread() = 0;
46
47 /**
48 * Sets the currently active thread when the debugger is stopped.
49 */
50 virtual void SetActiveThread(Kernel::KThread* thread) = 0;
51};
52
53class DebuggerFrontend {
54public:
55 explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
56
57 virtual ~DebuggerFrontend() = default;
58
59 /**
60 * Called after the client has successfully connected to the port.
61 */
62 virtual void Connected() = 0;
63
64 /**
65 * Called when emulation has stopped.
66 */
67 virtual void Stopped(Kernel::KThread* thread) = 0;
68
69 /**
70 * Called when new data is asynchronously received on the client socket.
71 * A list of actions to perform is returned.
72 */
73 [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
74
75protected:
76 DebuggerBackend& backend;
77};
78
79} // namespace Core
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
new file mode 100644
index 000000000..f52d78829
--- /dev/null
+++ b/src/core/debugger/gdbstub.cpp
@@ -0,0 +1,618 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <atomic>
5#include <numeric>
6#include <optional>
7#include <thread>
8
9#include <boost/algorithm/string.hpp>
10
11#include "common/hex_util.h"
12#include "common/logging/log.h"
13#include "common/scope_exit.h"
14#include "core/arm/arm_interface.h"
15#include "core/core.h"
16#include "core/debugger/gdbstub.h"
17#include "core/debugger/gdbstub_arch.h"
18#include "core/hle/kernel/k_page_table.h"
19#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/k_thread.h"
21#include "core/loader/loader.h"
22#include "core/memory.h"
23
24namespace Core {
25
26constexpr char GDB_STUB_START = '$';
27constexpr char GDB_STUB_END = '#';
28constexpr char GDB_STUB_ACK = '+';
29constexpr char GDB_STUB_NACK = '-';
30constexpr char GDB_STUB_INT3 = 0x03;
31constexpr int GDB_STUB_SIGTRAP = 5;
32
33constexpr char GDB_STUB_REPLY_ERR[] = "E01";
34constexpr char GDB_STUB_REPLY_OK[] = "OK";
35constexpr char GDB_STUB_REPLY_EMPTY[] = "";
36
37static u8 CalculateChecksum(std::string_view data) {
38 return std::accumulate(data.begin(), data.end(), u8{0},
39 [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
40}
41
42static std::string EscapeGDB(std::string_view data) {
43 std::string escaped;
44 escaped.reserve(data.size());
45
46 for (char c : data) {
47 switch (c) {
48 case '#':
49 escaped += "}\x03";
50 break;
51 case '$':
52 escaped += "}\x04";
53 break;
54 case '*':
55 escaped += "}\x0a";
56 break;
57 case '}':
58 escaped += "}\x5d";
59 break;
60 default:
61 escaped += c;
62 break;
63 }
64 }
65
66 return escaped;
67}
68
69static std::string EscapeXML(std::string_view data) {
70 std::string escaped;
71 escaped.reserve(data.size());
72
73 for (char c : data) {
74 switch (c) {
75 case '&':
76 escaped += "&amp;";
77 break;
78 case '"':
79 escaped += "&quot;";
80 break;
81 case '<':
82 escaped += "&lt;";
83 break;
84 case '>':
85 escaped += "&gt;";
86 break;
87 default:
88 escaped += c;
89 break;
90 }
91 }
92
93 return escaped;
94}
95
96GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
97 : DebuggerFrontend(backend_), system{system_} {
98 if (system.CurrentProcess()->Is64BitProcess()) {
99 arch = std::make_unique<GDBStubA64>();
100 } else {
101 arch = std::make_unique<GDBStubA32>();
102 }
103}
104
105GDBStub::~GDBStub() = default;
106
107void GDBStub::Connected() {}
108
109void GDBStub::Stopped(Kernel::KThread* thread) {
110 SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
111}
112
113std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
114 std::vector<DebuggerAction> actions;
115 current_command.insert(current_command.end(), data.begin(), data.end());
116
117 while (current_command.size() != 0) {
118 ProcessData(actions);
119 }
120
121 return actions;
122}
123
124void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
125 const char c{current_command[0]};
126
127 // Acknowledgement
128 if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
129 current_command.erase(current_command.begin());
130 return;
131 }
132
133 // Interrupt
134 if (c == GDB_STUB_INT3) {
135 LOG_INFO(Debug_GDBStub, "Received interrupt");
136 current_command.erase(current_command.begin());
137 actions.push_back(DebuggerAction::Interrupt);
138 SendStatus(GDB_STUB_ACK);
139 return;
140 }
141
142 // Otherwise, require the data to be the start of a command
143 if (c != GDB_STUB_START) {
144 LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
145 current_command.clear();
146 SendStatus(GDB_STUB_NACK);
147 return;
148 }
149
150 // Continue reading until command is complete
151 while (CommandEnd() == current_command.end()) {
152 const auto new_data{backend.ReadFromClient()};
153 current_command.insert(current_command.end(), new_data.begin(), new_data.end());
154 }
155
156 // Execute and respond to GDB
157 const auto command{DetachCommand()};
158
159 if (command) {
160 SendStatus(GDB_STUB_ACK);
161 ExecuteCommand(*command, actions);
162 } else {
163 SendStatus(GDB_STUB_NACK);
164 }
165}
166
167void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
168 LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
169
170 if (packet.length() == 0) {
171 SendReply(GDB_STUB_REPLY_ERR);
172 return;
173 }
174
175 if (packet.starts_with("vCont")) {
176 HandleVCont(packet.substr(5), actions);
177 return;
178 }
179
180 std::string_view command{packet.substr(1, packet.size())};
181
182 switch (packet[0]) {
183 case 'H': {
184 Kernel::KThread* thread{nullptr};
185 s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
186 if (thread_id >= 1) {
187 thread = GetThreadByID(thread_id);
188 } else {
189 thread = backend.GetActiveThread();
190 }
191
192 if (thread) {
193 SendReply(GDB_STUB_REPLY_OK);
194 backend.SetActiveThread(thread);
195 } else {
196 SendReply(GDB_STUB_REPLY_ERR);
197 }
198 break;
199 }
200 case 'T': {
201 s64 thread_id{strtoll(command.data(), nullptr, 16)};
202 if (GetThreadByID(thread_id)) {
203 SendReply(GDB_STUB_REPLY_OK);
204 } else {
205 SendReply(GDB_STUB_REPLY_ERR);
206 }
207 break;
208 }
209 case 'Q':
210 case 'q':
211 HandleQuery(command);
212 break;
213 case '?':
214 SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
215 break;
216 case 'k':
217 LOG_INFO(Debug_GDBStub, "Shutting down emulation");
218 actions.push_back(DebuggerAction::ShutdownEmulation);
219 break;
220 case 'g':
221 SendReply(arch->ReadRegisters(backend.GetActiveThread()));
222 break;
223 case 'G':
224 arch->WriteRegisters(backend.GetActiveThread(), command);
225 SendReply(GDB_STUB_REPLY_OK);
226 break;
227 case 'p': {
228 const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
229 SendReply(arch->RegRead(backend.GetActiveThread(), reg));
230 break;
231 }
232 case 'P': {
233 const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
234 const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
235 arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
236 break;
237 }
238 case 'm': {
239 const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
240 const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
241 const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
242
243 if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
244 std::vector<u8> mem(size);
245 system.Memory().ReadBlock(addr, mem.data(), size);
246
247 SendReply(Common::HexToString(mem));
248 } else {
249 SendReply(GDB_STUB_REPLY_ERR);
250 }
251 break;
252 }
253 case 'M': {
254 const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
255 const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
256
257 const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
258 const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
259
260 const auto mem_substr{std::string_view(command).substr(mem_sep)};
261 const auto mem{Common::HexStringToVector(mem_substr, false)};
262
263 if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
264 system.Memory().WriteBlock(addr, mem.data(), size);
265 system.InvalidateCpuInstructionCacheRange(addr, size);
266 SendReply(GDB_STUB_REPLY_OK);
267 } else {
268 SendReply(GDB_STUB_REPLY_ERR);
269 }
270 break;
271 }
272 case 's':
273 actions.push_back(DebuggerAction::StepThreadLocked);
274 break;
275 case 'C':
276 case 'c':
277 actions.push_back(DebuggerAction::Continue);
278 break;
279 case 'Z': {
280 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
281 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
282
283 if (system.Memory().IsValidVirtualAddress(addr)) {
284 replaced_instructions[addr] = system.Memory().Read32(addr);
285 system.Memory().Write32(addr, arch->BreakpointInstruction());
286 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
287
288 SendReply(GDB_STUB_REPLY_OK);
289 } else {
290 SendReply(GDB_STUB_REPLY_ERR);
291 }
292 break;
293 }
294 case 'z': {
295 const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
296 const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
297
298 const auto orig_insn{replaced_instructions.find(addr)};
299 if (system.Memory().IsValidVirtualAddress(addr) &&
300 orig_insn != replaced_instructions.end()) {
301 system.Memory().Write32(addr, orig_insn->second);
302 system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
303 replaced_instructions.erase(addr);
304
305 SendReply(GDB_STUB_REPLY_OK);
306 } else {
307 SendReply(GDB_STUB_REPLY_ERR);
308 }
309 break;
310 }
311 default:
312 SendReply(GDB_STUB_REPLY_EMPTY);
313 break;
314 }
315}
316
317// Structure offsets are from Atmosphere
318// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
319
320static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
321 const Kernel::KThread* thread) {
322 // Read thread type from TLS
323 const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
324 const VAddr argument_thread_type{thread->GetArgument()};
325
326 if (argument_thread_type && tls_thread_type != argument_thread_type) {
327 // Probably not created by nnsdk, no name available.
328 return std::nullopt;
329 }
330
331 if (!tls_thread_type) {
332 return std::nullopt;
333 }
334
335 const u16 version{memory.Read16(tls_thread_type + 0x26)};
336 VAddr name_pointer{};
337 if (version == 1) {
338 name_pointer = memory.Read32(tls_thread_type + 0xe4);
339 } else {
340 name_pointer = memory.Read32(tls_thread_type + 0xe8);
341 }
342
343 if (!name_pointer) {
344 // No name provided.
345 return std::nullopt;
346 }
347
348 return memory.ReadCString(name_pointer, 256);
349}
350
351static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
352 const Kernel::KThread* thread) {
353 // Read thread type from TLS
354 const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
355 const VAddr argument_thread_type{thread->GetArgument()};
356
357 if (argument_thread_type && tls_thread_type != argument_thread_type) {
358 // Probably not created by nnsdk, no name available.
359 return std::nullopt;
360 }
361
362 if (!tls_thread_type) {
363 return std::nullopt;
364 }
365
366 const u16 version{memory.Read16(tls_thread_type + 0x46)};
367 VAddr name_pointer{};
368 if (version == 1) {
369 name_pointer = memory.Read64(tls_thread_type + 0x1a0);
370 } else {
371 name_pointer = memory.Read64(tls_thread_type + 0x1a8);
372 }
373
374 if (!name_pointer) {
375 // No name provided.
376 return std::nullopt;
377 }
378
379 return memory.ReadCString(name_pointer, 256);
380}
381
382static std::optional<std::string> GetThreadName(Core::System& system,
383 const Kernel::KThread* thread) {
384 if (system.CurrentProcess()->Is64BitProcess()) {
385 return GetNameFromThreadType64(system.Memory(), thread);
386 } else {
387 return GetNameFromThreadType32(system.Memory(), thread);
388 }
389}
390
391static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
392 switch (thread->GetWaitReasonForDebugging()) {
393 case Kernel::ThreadWaitReasonForDebugging::Sleep:
394 return "Sleep";
395 case Kernel::ThreadWaitReasonForDebugging::IPC:
396 return "IPC";
397 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
398 return "Synchronization";
399 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
400 return "ConditionVar";
401 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
402 return "Arbitration";
403 case Kernel::ThreadWaitReasonForDebugging::Suspended:
404 return "Suspended";
405 default:
406 return "Unknown";
407 }
408}
409
410static std::string GetThreadState(const Kernel::KThread* thread) {
411 switch (thread->GetState()) {
412 case Kernel::ThreadState::Initialized:
413 return "Initialized";
414 case Kernel::ThreadState::Waiting:
415 return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
416 case Kernel::ThreadState::Runnable:
417 return "Runnable";
418 case Kernel::ThreadState::Terminated:
419 return "Terminated";
420 default:
421 return "Unknown";
422 }
423}
424
425static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
426 const auto amount{request.substr(request.find(',') + 1)};
427 const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
428 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
429
430 if (offset_val + amount_val > buffer.size()) {
431 return fmt::format("l{}", buffer.substr(offset_val));
432 } else {
433 return fmt::format("m{}", buffer.substr(offset_val, amount_val));
434 }
435}
436
437void GDBStub::HandleQuery(std::string_view command) {
438 if (command.starts_with("TStatus")) {
439 // no tracepoint support
440 SendReply("T0");
441 } else if (command.starts_with("Supported")) {
442 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
443 "vContSupported+;QStartNoAckMode+");
444 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
445 const auto target_xml{arch->GetTargetXML()};
446 SendReply(PaginateBuffer(target_xml, command.substr(30)));
447 } else if (command.starts_with("Offsets")) {
448 Loader::AppLoader::Modules modules;
449 system.GetAppLoader().ReadNSOModules(modules);
450
451 const auto main = std::find_if(modules.begin(), modules.end(),
452 [](const auto& key) { return key.second == "main"; });
453 if (main != modules.end()) {
454 SendReply(fmt::format("TextSeg={:x}", main->first));
455 } else {
456 SendReply(fmt::format("TextSeg={:x}",
457 system.CurrentProcess()->PageTable().GetCodeRegionStart()));
458 }
459 } else if (command.starts_with("Xfer:libraries:read::")) {
460 Loader::AppLoader::Modules modules;
461 system.GetAppLoader().ReadNSOModules(modules);
462
463 std::string buffer;
464 buffer += R"(<?xml version="1.0"?>)";
465 buffer += "<library-list>";
466 for (const auto& [base, name] : modules) {
467 buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
468 EscapeXML(name), base);
469 }
470 buffer += "</library-list>";
471
472 SendReply(PaginateBuffer(buffer, command.substr(21)));
473 } else if (command.starts_with("fThreadInfo")) {
474 // beginning of list
475 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
476 std::vector<std::string> thread_ids;
477 for (const auto& thread : threads) {
478 thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
479 }
480 SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
481 } else if (command.starts_with("sThreadInfo")) {
482 // end of list
483 SendReply("l");
484 } else if (command.starts_with("Xfer:threads:read::")) {
485 std::string buffer;
486 buffer += R"(<?xml version="1.0"?>)";
487 buffer += "<threads>";
488
489 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
490 for (const auto* thread : threads) {
491 auto thread_name{GetThreadName(system, thread)};
492 if (!thread_name) {
493 thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
494 }
495
496 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
497 thread->GetThreadID(), thread->GetActiveCore(),
498 EscapeXML(*thread_name), GetThreadState(thread));
499 }
500
501 buffer += "</threads>";
502
503 SendReply(PaginateBuffer(buffer, command.substr(19)));
504 } else if (command.starts_with("Attached")) {
505 SendReply("0");
506 } else if (command.starts_with("StartNoAckMode")) {
507 no_ack = true;
508 SendReply(GDB_STUB_REPLY_OK);
509 } else {
510 SendReply(GDB_STUB_REPLY_EMPTY);
511 }
512}
513
514void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
515 if (command == "?") {
516 // Continuing and stepping are supported
517 // (signal is ignored, but required for GDB to use vCont)
518 SendReply("vCont;c;C;s;S");
519 return;
520 }
521
522 Kernel::KThread* stepped_thread{nullptr};
523 bool lock_execution{true};
524
525 std::vector<std::string> entries;
526 boost::split(entries, command.substr(1), boost::is_any_of(";"));
527 for (const auto& thread_action : entries) {
528 std::vector<std::string> parts;
529 boost::split(parts, thread_action, boost::is_any_of(":"));
530
531 if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
532 lock_execution = false;
533 }
534 if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
535 stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
536 }
537 }
538
539 if (stepped_thread) {
540 backend.SetActiveThread(stepped_thread);
541 actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
542 : DebuggerAction::StepThreadUnlocked);
543 } else {
544 actions.push_back(DebuggerAction::Continue);
545 }
546}
547
548Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
549 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
550 for (auto* thread : threads) {
551 if (thread->GetThreadID() == thread_id) {
552 return thread;
553 }
554 }
555
556 return nullptr;
557}
558
559std::vector<char>::const_iterator GDBStub::CommandEnd() const {
560 // Find the end marker
561 const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
562
563 // Require the checksum to be present
564 return std::min(end + 2, current_command.end());
565}
566
567std::optional<std::string> GDBStub::DetachCommand() {
568 // Slice the string part from the beginning to the end marker
569 const auto end{CommandEnd()};
570
571 // Extract possible command data
572 std::string data(current_command.data(), end - current_command.begin() + 1);
573
574 // Shift over the remaining contents
575 current_command.erase(current_command.begin(), end + 1);
576
577 // Validate received command
578 if (data[0] != GDB_STUB_START) {
579 LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
580 return std::nullopt;
581 }
582
583 u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
584 u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
585
586 // Verify checksum
587 if (calculated != received) {
588 LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
589 calculated, received);
590 return std::nullopt;
591 }
592
593 return data.substr(1, data.size() - 4);
594}
595
596void GDBStub::SendReply(std::string_view data) {
597 const auto escaped{EscapeGDB(data)};
598 const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
599 CalculateChecksum(escaped))};
600 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
601
602 // C++ string support is complete rubbish
603 const u8* output_begin = reinterpret_cast<const u8*>(output.data());
604 const u8* output_end = output_begin + output.size();
605 backend.WriteToClient(std::span<const u8>(output_begin, output_end));
606}
607
608void GDBStub::SendStatus(char status) {
609 if (no_ack) {
610 return;
611 }
612
613 std::array<u8, 1> buf = {static_cast<u8>(status)};
614 LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
615 backend.WriteToClient(buf);
616}
617
618} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
new file mode 100644
index 000000000..1bb638187
--- /dev/null
+++ b/src/core/debugger/gdbstub.h
@@ -0,0 +1,48 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <map>
7#include <memory>
8#include <optional>
9#include <string_view>
10#include <vector>
11
12#include "core/debugger/debugger_interface.h"
13#include "core/debugger/gdbstub_arch.h"
14
15namespace Core {
16
17class System;
18
19class GDBStub : public DebuggerFrontend {
20public:
21 explicit GDBStub(DebuggerBackend& backend, Core::System& system);
22 ~GDBStub() override;
23
24 void Connected() override;
25 void Stopped(Kernel::KThread* thread) override;
26 std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
27
28private:
29 void ProcessData(std::vector<DebuggerAction>& actions);
30 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
31 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
32 void HandleQuery(std::string_view command);
33 std::vector<char>::const_iterator CommandEnd() const;
34 std::optional<std::string> DetachCommand();
35 Kernel::KThread* GetThreadByID(u64 thread_id);
36
37 void SendReply(std::string_view data);
38 void SendStatus(char status);
39
40private:
41 Core::System& system;
42 std::unique_ptr<GDBStubArch> arch;
43 std::vector<char> current_command;
44 std::map<VAddr, u32> replaced_instructions;
45 bool no_ack{};
46};
47
48} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
new file mode 100644
index 000000000..750c353b9
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -0,0 +1,483 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/hex_util.h"
5#include "core/debugger/gdbstub_arch.h"
6#include "core/hle/kernel/k_thread.h"
7
8namespace Core {
9
10template <typename T>
11static T HexToValue(std::string_view hex) {
12 static_assert(std::is_trivially_copyable_v<T>);
13 T value{};
14 const auto mem{Common::HexStringToVector(hex, false)};
15 std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
16 return value;
17}
18
19template <typename T>
20static std::string ValueToHex(const T value) {
21 static_assert(std::is_trivially_copyable_v<T>);
22 std::array<u8, sizeof(T)> mem{};
23 std::memcpy(mem.data(), &value, sizeof(T));
24 return Common::HexToString(mem);
25}
26
27template <typename T>
28static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
29 static_assert(std::is_trivially_copyable_v<T>);
30 T value{};
31 std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
32 sizeof(T));
33 return value;
34}
35
36template <typename T>
37static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
38 static_assert(std::is_trivially_copyable_v<T>);
39 std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
40}
41
42// For sample XML files see the GDB source /gdb/features
43// This XML defines what the registers are for this specific ARM device
44std::string GDBStubA64::GetTargetXML() const {
45 constexpr const char* target_xml =
46 R"(<?xml version="1.0"?>
47<!DOCTYPE target SYSTEM "gdb-target.dtd">
48<target version="1.0">
49 <architecture>aarch64</architecture>
50 <feature name="org.gnu.gdb.aarch64.core">
51 <reg name="x0" bitsize="64"/>
52 <reg name="x1" bitsize="64"/>
53 <reg name="x2" bitsize="64"/>
54 <reg name="x3" bitsize="64"/>
55 <reg name="x4" bitsize="64"/>
56 <reg name="x5" bitsize="64"/>
57 <reg name="x6" bitsize="64"/>
58 <reg name="x7" bitsize="64"/>
59 <reg name="x8" bitsize="64"/>
60 <reg name="x9" bitsize="64"/>
61 <reg name="x10" bitsize="64"/>
62 <reg name="x11" bitsize="64"/>
63 <reg name="x12" bitsize="64"/>
64 <reg name="x13" bitsize="64"/>
65 <reg name="x14" bitsize="64"/>
66 <reg name="x15" bitsize="64"/>
67 <reg name="x16" bitsize="64"/>
68 <reg name="x17" bitsize="64"/>
69 <reg name="x18" bitsize="64"/>
70 <reg name="x19" bitsize="64"/>
71 <reg name="x20" bitsize="64"/>
72 <reg name="x21" bitsize="64"/>
73 <reg name="x22" bitsize="64"/>
74 <reg name="x23" bitsize="64"/>
75 <reg name="x24" bitsize="64"/>
76 <reg name="x25" bitsize="64"/>
77 <reg name="x26" bitsize="64"/>
78 <reg name="x27" bitsize="64"/>
79 <reg name="x28" bitsize="64"/>
80 <reg name="x29" bitsize="64"/>
81 <reg name="x30" bitsize="64"/>
82 <reg name="sp" bitsize="64" type="data_ptr"/>
83 <reg name="pc" bitsize="64" type="code_ptr"/>
84 <flags id="cpsr_flags" size="4">
85 <field name="SP" start="0" end="0"/>
86 <field name="" start="1" end="1"/>
87 <field name="EL" start="2" end="3"/>
88 <field name="nRW" start="4" end="4"/>
89 <field name="" start="5" end="5"/>
90 <field name="F" start="6" end="6"/>
91 <field name="I" start="7" end="7"/>
92 <field name="A" start="8" end="8"/>
93 <field name="D" start="9" end="9"/>
94 <field name="IL" start="20" end="20"/>
95 <field name="SS" start="21" end="21"/>
96 <field name="V" start="28" end="28"/>
97 <field name="C" start="29" end="29"/>
98 <field name="Z" start="30" end="30"/>
99 <field name="N" start="31" end="31"/>
100 </flags>
101 <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
102 </feature>
103 <feature name="org.gnu.gdb.aarch64.fpu">
104 <vector id="v2d" type="ieee_double" count="2"/>
105 <vector id="v2u" type="uint64" count="2"/>
106 <vector id="v2i" type="int64" count="2"/>
107 <vector id="v4f" type="ieee_single" count="4"/>
108 <vector id="v4u" type="uint32" count="4"/>
109 <vector id="v4i" type="int32" count="4"/>
110 <vector id="v8u" type="uint16" count="8"/>
111 <vector id="v8i" type="int16" count="8"/>
112 <vector id="v16u" type="uint8" count="16"/>
113 <vector id="v16i" type="int8" count="16"/>
114 <vector id="v1u" type="uint128" count="1"/>
115 <vector id="v1i" type="int128" count="1"/>
116 <union id="vnd">
117 <field name="f" type="v2d"/>
118 <field name="u" type="v2u"/>
119 <field name="s" type="v2i"/>
120 </union>
121 <union id="vns">
122 <field name="f" type="v4f"/>
123 <field name="u" type="v4u"/>
124 <field name="s" type="v4i"/>
125 </union>
126 <union id="vnh">
127 <field name="u" type="v8u"/>
128 <field name="s" type="v8i"/>
129 </union>
130 <union id="vnb">
131 <field name="u" type="v16u"/>
132 <field name="s" type="v16i"/>
133 </union>
134 <union id="vnq">
135 <field name="u" type="v1u"/>
136 <field name="s" type="v1i"/>
137 </union>
138 <union id="aarch64v">
139 <field name="d" type="vnd"/>
140 <field name="s" type="vns"/>
141 <field name="h" type="vnh"/>
142 <field name="b" type="vnb"/>
143 <field name="q" type="vnq"/>
144 </union>
145 <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
146 <reg name="v1" bitsize="128" type="aarch64v" />
147 <reg name="v2" bitsize="128" type="aarch64v" />
148 <reg name="v3" bitsize="128" type="aarch64v" />
149 <reg name="v4" bitsize="128" type="aarch64v" />
150 <reg name="v5" bitsize="128" type="aarch64v" />
151 <reg name="v6" bitsize="128" type="aarch64v" />
152 <reg name="v7" bitsize="128" type="aarch64v" />
153 <reg name="v8" bitsize="128" type="aarch64v" />
154 <reg name="v9" bitsize="128" type="aarch64v" />
155 <reg name="v10" bitsize="128" type="aarch64v"/>
156 <reg name="v11" bitsize="128" type="aarch64v"/>
157 <reg name="v12" bitsize="128" type="aarch64v"/>
158 <reg name="v13" bitsize="128" type="aarch64v"/>
159 <reg name="v14" bitsize="128" type="aarch64v"/>
160 <reg name="v15" bitsize="128" type="aarch64v"/>
161 <reg name="v16" bitsize="128" type="aarch64v"/>
162 <reg name="v17" bitsize="128" type="aarch64v"/>
163 <reg name="v18" bitsize="128" type="aarch64v"/>
164 <reg name="v19" bitsize="128" type="aarch64v"/>
165 <reg name="v20" bitsize="128" type="aarch64v"/>
166 <reg name="v21" bitsize="128" type="aarch64v"/>
167 <reg name="v22" bitsize="128" type="aarch64v"/>
168 <reg name="v23" bitsize="128" type="aarch64v"/>
169 <reg name="v24" bitsize="128" type="aarch64v"/>
170 <reg name="v25" bitsize="128" type="aarch64v"/>
171 <reg name="v26" bitsize="128" type="aarch64v"/>
172 <reg name="v27" bitsize="128" type="aarch64v"/>
173 <reg name="v28" bitsize="128" type="aarch64v"/>
174 <reg name="v29" bitsize="128" type="aarch64v"/>
175 <reg name="v30" bitsize="128" type="aarch64v"/>
176 <reg name="v31" bitsize="128" type="aarch64v"/>
177 <reg name="fpsr" bitsize="32"/>
178 <reg name="fpcr" bitsize="32"/>
179 </feature>
180</target>)";
181
182 return target_xml;
183}
184
185std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
186 if (!thread) {
187 return "";
188 }
189
190 const auto& context{thread->GetContext64()};
191 const auto& gprs{context.cpu_registers};
192 const auto& fprs{context.vector_registers};
193
194 if (id <= SP_REGISTER) {
195 return ValueToHex(gprs[id]);
196 } else if (id == PC_REGISTER) {
197 return ValueToHex(context.pc);
198 } else if (id == PSTATE_REGISTER) {
199 return ValueToHex(context.pstate);
200 } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
201 return ValueToHex(fprs[id - Q0_REGISTER]);
202 } else if (id == FPSR_REGISTER) {
203 return ValueToHex(context.fpsr);
204 } else if (id == FPCR_REGISTER) {
205 return ValueToHex(context.fpcr);
206 } else {
207 return "";
208 }
209}
210
211void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
212 if (!thread) {
213 return;
214 }
215
216 auto& context{thread->GetContext64()};
217
218 if (id <= SP_REGISTER) {
219 context.cpu_registers[id] = HexToValue<u64>(value);
220 } else if (id == PC_REGISTER) {
221 context.pc = HexToValue<u64>(value);
222 } else if (id == PSTATE_REGISTER) {
223 context.pstate = HexToValue<u32>(value);
224 } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
225 context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
226 } else if (id == FPSR_REGISTER) {
227 context.fpsr = HexToValue<u32>(value);
228 } else if (id == FPCR_REGISTER) {
229 context.fpcr = HexToValue<u32>(value);
230 }
231}
232
233std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
234 std::string output;
235
236 for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
237 output += RegRead(thread, reg);
238 }
239
240 return output;
241}
242
243void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
244 for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
245 if (reg <= SP_REGISTER || reg == PC_REGISTER) {
246 RegWrite(thread, reg, register_data.substr(i, 16));
247 i += 16;
248 } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
249 RegWrite(thread, reg, register_data.substr(i, 8));
250 i += 8;
251 } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
252 RegWrite(thread, reg, register_data.substr(i, 32));
253 i += 32;
254 }
255 }
256}
257
258std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
259 return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
260 RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
261 LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
262}
263
264u32 GDBStubA64::BreakpointInstruction() const {
265 // A64: brk #0
266 return 0xd4200000;
267}
268
269std::string GDBStubA32::GetTargetXML() const {
270 constexpr const char* target_xml =
271 R"(<?xml version="1.0"?>
272<!DOCTYPE target SYSTEM "gdb-target.dtd">
273<target version="1.0">
274 <architecture>arm</architecture>
275 <feature name="org.gnu.gdb.arm.core">
276 <reg name="r0" bitsize="32" type="uint32"/>
277 <reg name="r1" bitsize="32" type="uint32"/>
278 <reg name="r2" bitsize="32" type="uint32"/>
279 <reg name="r3" bitsize="32" type="uint32"/>
280 <reg name="r4" bitsize="32" type="uint32"/>
281 <reg name="r5" bitsize="32" type="uint32"/>
282 <reg name="r6" bitsize="32" type="uint32"/>
283 <reg name="r7" bitsize="32" type="uint32"/>
284 <reg name="r8" bitsize="32" type="uint32"/>
285 <reg name="r9" bitsize="32" type="uint32"/>
286 <reg name="r10" bitsize="32" type="uint32"/>
287 <reg name="r11" bitsize="32" type="uint32"/>
288 <reg name="r12" bitsize="32" type="uint32"/>
289 <reg name="sp" bitsize="32" type="data_ptr"/>
290 <reg name="lr" bitsize="32" type="code_ptr"/>
291 <reg name="pc" bitsize="32" type="code_ptr"/>
292 <!-- The CPSR is register 25, rather than register 16, because
293 the FPA registers historically were placed between the PC
294 and the CPSR in the "g" packet. -->
295 <reg name="cpsr" bitsize="32" regnum="25"/>
296 </feature>
297 <feature name="org.gnu.gdb.arm.vfp">
298 <vector id="neon_uint8x8" type="uint8" count="8"/>
299 <vector id="neon_uint16x4" type="uint16" count="4"/>
300 <vector id="neon_uint32x2" type="uint32" count="2"/>
301 <vector id="neon_float32x2" type="ieee_single" count="2"/>
302 <union id="neon_d">
303 <field name="u8" type="neon_uint8x8"/>
304 <field name="u16" type="neon_uint16x4"/>
305 <field name="u32" type="neon_uint32x2"/>
306 <field name="u64" type="uint64"/>
307 <field name="f32" type="neon_float32x2"/>
308 <field name="f64" type="ieee_double"/>
309 </union>
310 <vector id="neon_uint8x16" type="uint8" count="16"/>
311 <vector id="neon_uint16x8" type="uint16" count="8"/>
312 <vector id="neon_uint32x4" type="uint32" count="4"/>
313 <vector id="neon_uint64x2" type="uint64" count="2"/>
314 <vector id="neon_float32x4" type="ieee_single" count="4"/>
315 <vector id="neon_float64x2" type="ieee_double" count="2"/>
316 <union id="neon_q">
317 <field name="u8" type="neon_uint8x16"/>
318 <field name="u16" type="neon_uint16x8"/>
319 <field name="u32" type="neon_uint32x4"/>
320 <field name="u64" type="neon_uint64x2"/>
321 <field name="f32" type="neon_float32x4"/>
322 <field name="f64" type="neon_float64x2"/>
323 </union>
324 <reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
325 <reg name="d1" bitsize="64" type="neon_d"/>
326 <reg name="d2" bitsize="64" type="neon_d"/>
327 <reg name="d3" bitsize="64" type="neon_d"/>
328 <reg name="d4" bitsize="64" type="neon_d"/>
329 <reg name="d5" bitsize="64" type="neon_d"/>
330 <reg name="d6" bitsize="64" type="neon_d"/>
331 <reg name="d7" bitsize="64" type="neon_d"/>
332 <reg name="d8" bitsize="64" type="neon_d"/>
333 <reg name="d9" bitsize="64" type="neon_d"/>
334 <reg name="d10" bitsize="64" type="neon_d"/>
335 <reg name="d11" bitsize="64" type="neon_d"/>
336 <reg name="d12" bitsize="64" type="neon_d"/>
337 <reg name="d13" bitsize="64" type="neon_d"/>
338 <reg name="d14" bitsize="64" type="neon_d"/>
339 <reg name="d15" bitsize="64" type="neon_d"/>
340 <reg name="d16" bitsize="64" type="neon_d"/>
341 <reg name="d17" bitsize="64" type="neon_d"/>
342 <reg name="d18" bitsize="64" type="neon_d"/>
343 <reg name="d19" bitsize="64" type="neon_d"/>
344 <reg name="d20" bitsize="64" type="neon_d"/>
345 <reg name="d21" bitsize="64" type="neon_d"/>
346 <reg name="d22" bitsize="64" type="neon_d"/>
347 <reg name="d23" bitsize="64" type="neon_d"/>
348 <reg name="d24" bitsize="64" type="neon_d"/>
349 <reg name="d25" bitsize="64" type="neon_d"/>
350 <reg name="d26" bitsize="64" type="neon_d"/>
351 <reg name="d27" bitsize="64" type="neon_d"/>
352 <reg name="d28" bitsize="64" type="neon_d"/>
353 <reg name="d29" bitsize="64" type="neon_d"/>
354 <reg name="d30" bitsize="64" type="neon_d"/>
355 <reg name="d31" bitsize="64" type="neon_d"/>
356
357 <reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
358 <reg name="q1" bitsize="128" type="neon_q"/>
359 <reg name="q2" bitsize="128" type="neon_q"/>
360 <reg name="q3" bitsize="128" type="neon_q"/>
361 <reg name="q4" bitsize="128" type="neon_q"/>
362 <reg name="q5" bitsize="128" type="neon_q"/>
363 <reg name="q6" bitsize="128" type="neon_q"/>
364 <reg name="q7" bitsize="128" type="neon_q"/>
365 <reg name="q8" bitsize="128" type="neon_q"/>
366 <reg name="q9" bitsize="128" type="neon_q"/>
367 <reg name="q10" bitsize="128" type="neon_q"/>
368 <reg name="q10" bitsize="128" type="neon_q"/>
369 <reg name="q12" bitsize="128" type="neon_q"/>
370 <reg name="q13" bitsize="128" type="neon_q"/>
371 <reg name="q14" bitsize="128" type="neon_q"/>
372 <reg name="q15" bitsize="128" type="neon_q"/>
373
374 <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
375 </feature>
376</target>)";
377
378 return target_xml;
379}
380
381std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
382 if (!thread) {
383 return "";
384 }
385
386 const auto& context{thread->GetContext32()};
387 const auto& gprs{context.cpu_registers};
388 const auto& fprs{context.extension_registers};
389
390 if (id <= PC_REGISTER) {
391 return ValueToHex(gprs[id]);
392 } else if (id == CPSR_REGISTER) {
393 return ValueToHex(context.cpsr);
394 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
395 const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
396 return ValueToHex(dN);
397 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
398 const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
399 return ValueToHex(qN);
400 } else if (id == FPSCR_REGISTER) {
401 return ValueToHex(context.fpscr);
402 } else {
403 return "";
404 }
405}
406
407void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
408 if (!thread) {
409 return;
410 }
411
412 auto& context{thread->GetContext32()};
413 auto& fprs{context.extension_registers};
414
415 if (id <= PC_REGISTER) {
416 context.cpu_registers[id] = HexToValue<u32>(value);
417 } else if (id == CPSR_REGISTER) {
418 context.cpsr = HexToValue<u32>(value);
419 } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
420 PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
421 } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
422 PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
423 } else if (id == FPSCR_REGISTER) {
424 context.fpscr = HexToValue<u32>(value);
425 }
426}
427
428std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
429 std::string output;
430
431 for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
432 const bool gpr{reg <= PC_REGISTER};
433 const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
434 const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
435
436 if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
437 continue;
438 }
439
440 output += RegRead(thread, reg);
441 }
442
443 return output;
444}
445
446void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
447 for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
448 const bool gpr{reg <= PC_REGISTER};
449 const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
450 const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
451
452 if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
453 RegWrite(thread, reg, register_data.substr(i, 8));
454 i += 8;
455 } else if (dfpr) {
456 RegWrite(thread, reg, register_data.substr(i, 16));
457 i += 16;
458 } else if (qfpr) {
459 RegWrite(thread, reg, register_data.substr(i, 32));
460 i += 32;
461 }
462
463 if (reg == PC_REGISTER) {
464 reg = CPSR_REGISTER - 1;
465 } else if (reg == CPSR_REGISTER) {
466 reg = D0_REGISTER - 1;
467 }
468 }
469}
470
471std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
472 return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
473 RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
474 LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
475}
476
477u32 GDBStubA32::BreakpointInstruction() const {
478 // A32: trap
479 // T32: trap + b #4
480 return 0xe7ffdefe;
481}
482
483} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h
new file mode 100644
index 000000000..4d039a9f7
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.h
@@ -0,0 +1,67 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9
10namespace Kernel {
11class KThread;
12}
13
14namespace Core {
15
16class GDBStubArch {
17public:
18 virtual std::string GetTargetXML() const = 0;
19 virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
20 virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
21 virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
22 virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
23 virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
24 virtual u32 BreakpointInstruction() const = 0;
25};
26
27class GDBStubA64 final : public GDBStubArch {
28public:
29 std::string GetTargetXML() const override;
30 std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
31 void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
32 std::string ReadRegisters(const Kernel::KThread* thread) const override;
33 void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
34 std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
35 u32 BreakpointInstruction() const override;
36
37private:
38 static constexpr u32 LR_REGISTER = 30;
39 static constexpr u32 SP_REGISTER = 31;
40 static constexpr u32 PC_REGISTER = 32;
41 static constexpr u32 PSTATE_REGISTER = 33;
42 static constexpr u32 Q0_REGISTER = 34;
43 static constexpr u32 FPSR_REGISTER = 66;
44 static constexpr u32 FPCR_REGISTER = 67;
45};
46
47class GDBStubA32 final : public GDBStubArch {
48public:
49 std::string GetTargetXML() const override;
50 std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
51 void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
52 std::string ReadRegisters(const Kernel::KThread* thread) const override;
53 void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
54 std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
55 u32 BreakpointInstruction() const override;
56
57private:
58 static constexpr u32 SP_REGISTER = 13;
59 static constexpr u32 LR_REGISTER = 14;
60 static constexpr u32 PC_REGISTER = 15;
61 static constexpr u32 CPSR_REGISTER = 25;
62 static constexpr u32 D0_REGISTER = 32;
63 static constexpr u32 Q0_REGISTER = 64;
64 static constexpr u32 FPSCR_REGISTER = 80;
65};
66
67} // namespace Core
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 26ec1091b..9f76f9bcb 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -498,6 +498,49 @@ struct SixAxisSensorFusionParameters {
498static_assert(sizeof(SixAxisSensorFusionParameters) == 8, 498static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
499 "SixAxisSensorFusionParameters is an invalid size"); 499 "SixAxisSensorFusionParameters is an invalid size");
500 500
501// This is nn::hid::server::SixAxisSensorProperties
502struct SixAxisSensorProperties {
503 union {
504 u8 raw{};
505 BitField<0, 1, u8> is_newly_assigned;
506 BitField<1, 1, u8> is_firmware_update_available;
507 };
508};
509static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
510
511// This is nn::hid::SixAxisSensorCalibrationParameter
512struct SixAxisSensorCalibrationParameter {
513 std::array<u8, 0x744> unknown_data{};
514};
515static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
516 "SixAxisSensorCalibrationParameter is an invalid size");
517
518// This is nn::hid::SixAxisSensorIcInformation
519struct SixAxisSensorIcInformation {
520 f32 angular_rate{2000.0f}; // dps
521 std::array<f32, 6> unknown_gyro_data1{
522 -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
523 }; // dps
524 std::array<f32, 9> unknown_gyro_data2{
525 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
526 };
527 std::array<f32, 9> unknown_gyro_data3{
528 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
529 };
530 f32 acceleration_range{8.0f}; // g force
531 std::array<f32, 6> unknown_accel_data1{
532 -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
533 }; // g force
534 std::array<f32, 9> unknown_accel_data2{
535 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
536 };
537 std::array<f32, 9> unknown_accel_data3{
538 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
539 };
540};
541static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
542 "SixAxisSensorIcInformation is an invalid size");
543
501// This is nn::hid::VibrationDeviceHandle 544// This is nn::hid::VibrationDeviceHandle
502struct VibrationDeviceHandle { 545struct VibrationDeviceHandle {
503 NpadStyleIndex npad_type{NpadStyleIndex::None}; 546 NpadStyleIndex npad_type{NpadStyleIndex::None};
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 490e31fc7..dcfeacccd 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -64,6 +64,10 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
64 { 64 {
65 KScopedSchedulerLock lock{kernel}; 65 KScopedSchedulerLock lock{kernel};
66 thread->SetState(ThreadState::Runnable); 66 thread->SetState(ThreadState::Runnable);
67
68 if (system.DebuggerEnabled()) {
69 thread->RequestSuspend(SuspendType::Debug);
70 }
67 } 71 }
68} 72}
69} // Anonymous namespace 73} // Anonymous namespace
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index ab9ce6a86..940334f59 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
198 resource_limit_release_hint = false; 198 resource_limit_release_hint = false;
199 cpu_time = 0; 199 cpu_time = 0;
200 200
201 // Set debug context.
202 stack_top = user_stack_top;
203 argument = arg;
204
201 // Clear our stack parameters. 205 // Clear our stack parameters.
202 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, 206 std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
203 sizeof(StackParameters)); 207 sizeof(StackParameters));
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index b55a922ab..f4d83f99a 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -100,6 +100,12 @@ enum class ThreadWaitReasonForDebugging : u32 {
100 Suspended, ///< Thread is waiting due to process suspension 100 Suspended, ///< Thread is waiting due to process suspension
101}; 101};
102 102
103enum class StepState : u32 {
104 NotStepping, ///< Thread is not currently stepping
105 StepPending, ///< Thread will step when next scheduled
106 StepPerformed, ///< Thread has stepped, waiting to be scheduled again
107};
108
103[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); 109[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
104[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); 110[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
105[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); 111[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -267,6 +273,14 @@ public:
267 273
268 void SetState(ThreadState state); 274 void SetState(ThreadState state);
269 275
276 [[nodiscard]] StepState GetStepState() const {
277 return step_state;
278 }
279
280 void SetStepState(StepState state) {
281 step_state = state;
282 }
283
270 [[nodiscard]] s64 GetLastScheduledTick() const { 284 [[nodiscard]] s64 GetLastScheduledTick() const {
271 return last_scheduled_tick; 285 return last_scheduled_tick;
272 } 286 }
@@ -646,6 +660,14 @@ public:
646 void IfDummyThreadTryWait(); 660 void IfDummyThreadTryWait();
647 void IfDummyThreadEndWait(); 661 void IfDummyThreadEndWait();
648 662
663 [[nodiscard]] uintptr_t GetArgument() const {
664 return argument;
665 }
666
667 [[nodiscard]] VAddr GetUserStackTop() const {
668 return stack_top;
669 }
670
649private: 671private:
650 static constexpr size_t PriorityInheritanceCountMax = 10; 672 static constexpr size_t PriorityInheritanceCountMax = 10;
651 union SyncObjectBuffer { 673 union SyncObjectBuffer {
@@ -769,6 +791,7 @@ private:
769 std::shared_ptr<Common::Fiber> host_context{}; 791 std::shared_ptr<Common::Fiber> host_context{};
770 bool is_single_core{}; 792 bool is_single_core{};
771 ThreadType thread_type{}; 793 ThreadType thread_type{};
794 StepState step_state{};
772 std::mutex dummy_wait_lock; 795 std::mutex dummy_wait_lock;
773 std::condition_variable dummy_wait_cv; 796 std::condition_variable dummy_wait_cv;
774 797
@@ -776,6 +799,8 @@ private:
776 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 799 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
777 VAddr mutex_wait_address_for_debugging{}; 800 VAddr mutex_wait_address_for_debugging{};
778 ThreadWaitReasonForDebugging wait_reason_for_debugging{}; 801 ThreadWaitReasonForDebugging wait_reason_for_debugging{};
802 uintptr_t argument;
803 VAddr stack_top;
779 804
780public: 805public:
781 using ConditionVariableThreadTreeType = ConditionVariableThreadTree; 806 using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 3eae1ae35..32e0708ba 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
61 } 61 }
62 62
63 last_update_timestamp = shared_memory->gesture_lifo.timestamp; 63 last_update_timestamp = shared_memory->gesture_lifo.timestamp;
64 UpdateGestureSharedMemory(gesture, time_difference);
64} 65}
65 66
66void Controller_Gesture::ReadTouchInput() { 67void Controller_Gesture::ReadTouchInput() {
@@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
94 return false; 95 return false;
95} 96}
96 97
97void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, 98void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
98 GestureProperties& gesture,
99 f32 time_difference) { 99 f32 time_difference) {
100 GestureType type = GestureType::Idle; 100 GestureType type = GestureType::Idle;
101 GestureAttribute attributes{}; 101 GestureAttribute attributes{};
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index c62a341bf..0d6099ea0 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -107,8 +107,7 @@ private:
107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); 107 bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
108 108
109 // Updates the shared memory to the next state 109 // Updates the shared memory to the next state
110 void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, 110 void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
111 f32 time_difference);
112 111
113 // Initializes new gesture 112 // Initializes new gesture
114 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); 113 void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index de06e1735..1e04ee3f2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -56,11 +56,22 @@ bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle
56 return npad_id && npad_type && device_index; 56 return npad_id && npad_type && device_index;
57} 57}
58 58
59bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { 59ResultCode Controller_NPad::VerifyValidSixAxisSensorHandle(
60 const Core::HID::SixAxisSensorHandle& device_handle) {
60 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)); 61 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
61 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType; 62 if (!npad_id) {
63 return InvalidNpadId;
64 }
62 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 65 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
63 return npad_id && npad_type && device_index; 66 if (!device_index) {
67 return NpadDeviceIndexOutOfRange;
68 }
69 // This doesn't get validated on nnsdk
70 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
71 if (!npad_type) {
72 return NpadInvalidHandle;
73 }
74 return ResultSuccess;
64} 75}
65 76
66Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_, 77Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
@@ -158,6 +169,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
158 shared_memory->system_properties.use_plus.Assign(1); 169 shared_memory->system_properties.use_plus.Assign(1);
159 shared_memory->system_properties.use_minus.Assign(1); 170 shared_memory->system_properties.use_minus.Assign(1);
160 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController; 171 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
172 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
161 break; 173 break;
162 case Core::HID::NpadStyleIndex::Handheld: 174 case Core::HID::NpadStyleIndex::Handheld:
163 shared_memory->style_tag.handheld.Assign(1); 175 shared_memory->style_tag.handheld.Assign(1);
@@ -170,16 +182,19 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
170 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual; 182 shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
171 shared_memory->applet_nfc_xcd.applet_footer.type = 183 shared_memory->applet_nfc_xcd.applet_footer.type =
172 AppletFooterUiType::HandheldJoyConLeftJoyConRight; 184 AppletFooterUiType::HandheldJoyConLeftJoyConRight;
185 shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
173 break; 186 break;
174 case Core::HID::NpadStyleIndex::JoyconDual: 187 case Core::HID::NpadStyleIndex::JoyconDual:
175 shared_memory->style_tag.joycon_dual.Assign(1); 188 shared_memory->style_tag.joycon_dual.Assign(1);
176 if (controller.is_dual_left_connected) { 189 if (controller.is_dual_left_connected) {
177 shared_memory->device_type.joycon_left.Assign(1); 190 shared_memory->device_type.joycon_left.Assign(1);
178 shared_memory->system_properties.use_minus.Assign(1); 191 shared_memory->system_properties.use_minus.Assign(1);
192 shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
179 } 193 }
180 if (controller.is_dual_right_connected) { 194 if (controller.is_dual_right_connected) {
181 shared_memory->device_type.joycon_right.Assign(1); 195 shared_memory->device_type.joycon_right.Assign(1);
182 shared_memory->system_properties.use_plus.Assign(1); 196 shared_memory->system_properties.use_plus.Assign(1);
197 shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
183 } 198 }
184 shared_memory->system_properties.use_directional_buttons.Assign(1); 199 shared_memory->system_properties.use_directional_buttons.Assign(1);
185 shared_memory->system_properties.is_vertical.Assign(1); 200 shared_memory->system_properties.is_vertical.Assign(1);
@@ -198,6 +213,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
198 shared_memory->system_properties.is_horizontal.Assign(1); 213 shared_memory->system_properties.is_horizontal.Assign(1);
199 shared_memory->system_properties.use_minus.Assign(1); 214 shared_memory->system_properties.use_minus.Assign(1);
200 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal; 215 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
216 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
201 break; 217 break;
202 case Core::HID::NpadStyleIndex::JoyconRight: 218 case Core::HID::NpadStyleIndex::JoyconRight:
203 shared_memory->style_tag.joycon_right.Assign(1); 219 shared_memory->style_tag.joycon_right.Assign(1);
@@ -205,6 +221,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
205 shared_memory->system_properties.is_horizontal.Assign(1); 221 shared_memory->system_properties.is_horizontal.Assign(1);
206 shared_memory->system_properties.use_plus.Assign(1); 222 shared_memory->system_properties.use_plus.Assign(1);
207 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal; 223 shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
224 shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
208 break; 225 break;
209 case Core::HID::NpadStyleIndex::GameCube: 226 case Core::HID::NpadStyleIndex::GameCube:
210 shared_memory->style_tag.gamecube.Assign(1); 227 shared_memory->style_tag.gamecube.Assign(1);
@@ -215,6 +232,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
215 case Core::HID::NpadStyleIndex::Pokeball: 232 case Core::HID::NpadStyleIndex::Pokeball:
216 shared_memory->style_tag.palma.Assign(1); 233 shared_memory->style_tag.palma.Assign(1);
217 shared_memory->device_type.palma.Assign(1); 234 shared_memory->device_type.palma.Assign(1);
235 shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
218 break; 236 break;
219 case Core::HID::NpadStyleIndex::NES: 237 case Core::HID::NpadStyleIndex::NES:
220 shared_memory->style_tag.lark.Assign(1); 238 shared_memory->style_tag.lark.Assign(1);
@@ -582,6 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
582 UNREACHABLE(); 600 UNREACHABLE();
583 break; 601 break;
584 case Core::HID::NpadStyleIndex::ProController: 602 case Core::HID::NpadStyleIndex::ProController:
603 case Core::HID::NpadStyleIndex::Pokeball:
585 set_motion_state(sixaxis_fullkey_state, motion_state[0]); 604 set_motion_state(sixaxis_fullkey_state, motion_state[0]);
586 break; 605 break;
587 case Core::HID::NpadStyleIndex::Handheld: 606 case Core::HID::NpadStyleIndex::Handheld:
@@ -672,6 +691,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
672} 691}
673 692
674void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) { 693void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
694 if (joy_hold_type != NpadJoyHoldType::Horizontal &&
695 joy_hold_type != NpadJoyHoldType::Vertical) {
696 LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
697 joy_hold_type);
698 return;
699 }
675 hold_type = joy_hold_type; 700 hold_type = joy_hold_type;
676} 701}
677 702
@@ -695,11 +720,12 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
695 return communication_mode; 720 return communication_mode;
696} 721}
697 722
698void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, 723ResultCode Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
699 NpadJoyAssignmentMode assignment_mode) { 724 NpadJoyDeviceType npad_device_type,
725 NpadJoyAssignmentMode assignment_mode) {
700 if (!IsNpadIdValid(npad_id)) { 726 if (!IsNpadIdValid(npad_id)) {
701 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 727 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
702 return; 728 return InvalidNpadId;
703 } 729 }
704 730
705 auto& controller = GetControllerFromNpadIdType(npad_id); 731 auto& controller = GetControllerFromNpadIdType(npad_id);
@@ -708,7 +734,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
708 } 734 }
709 735
710 if (!controller.device->IsConnected()) { 736 if (!controller.device->IsConnected()) {
711 return; 737 return ResultSuccess;
712 } 738 }
713 739
714 if (assignment_mode == NpadJoyAssignmentMode::Dual) { 740 if (assignment_mode == NpadJoyAssignmentMode::Dual) {
@@ -717,34 +743,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
717 controller.is_dual_left_connected = true; 743 controller.is_dual_left_connected = true;
718 controller.is_dual_right_connected = false; 744 controller.is_dual_right_connected = false;
719 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 745 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
720 return; 746 return ResultSuccess;
721 } 747 }
722 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) { 748 if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
723 DisconnectNpad(npad_id); 749 DisconnectNpad(npad_id);
724 controller.is_dual_left_connected = false; 750 controller.is_dual_left_connected = false;
725 controller.is_dual_right_connected = true; 751 controller.is_dual_right_connected = true;
726 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true); 752 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
727 return; 753 return ResultSuccess;
728 } 754 }
729 return; 755 return ResultSuccess;
730 } 756 }
731 757
732 // This is for NpadJoyAssignmentMode::Single 758 // This is for NpadJoyAssignmentMode::Single
733 759
734 // Only JoyconDual get affected by this function 760 // Only JoyconDual get affected by this function
735 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) { 761 if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
736 return; 762 return ResultSuccess;
737 } 763 }
738 764
739 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) { 765 if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
740 DisconnectNpad(npad_id); 766 DisconnectNpad(npad_id);
741 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true); 767 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
742 return; 768 return ResultSuccess;
743 } 769 }
744 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) { 770 if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
745 DisconnectNpad(npad_id); 771 DisconnectNpad(npad_id);
746 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true); 772 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
747 return; 773 return ResultSuccess;
748 } 774 }
749 775
750 // We have two controllers connected to the same npad_id we need to split them 776 // We have two controllers connected to the same npad_id we need to split them
@@ -762,6 +788,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
762 controller_2.is_dual_right_connected = false; 788 controller_2.is_dual_right_connected = false;
763 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true); 789 UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
764 } 790 }
791 return ResultSuccess;
765} 792}
766 793
767bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, 794bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -957,10 +984,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
957 InitNewlyAddedController(npad_id); 984 InitNewlyAddedController(npad_id);
958} 985}
959 986
960void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) { 987ResultCode Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
961 if (!IsNpadIdValid(npad_id)) { 988 if (!IsNpadIdValid(npad_id)) {
962 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 989 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
963 return; 990 return InvalidNpadId;
964 } 991 }
965 992
966 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id); 993 LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
@@ -977,6 +1004,12 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
977 shared_memory->device_type.raw = 0; 1004 shared_memory->device_type.raw = 0;
978 shared_memory->system_properties.raw = 0; 1005 shared_memory->system_properties.raw = 0;
979 shared_memory->button_properties.raw = 0; 1006 shared_memory->button_properties.raw = 0;
1007 shared_memory->sixaxis_fullkey_properties.raw = 0;
1008 shared_memory->sixaxis_handheld_properties.raw = 0;
1009 shared_memory->sixaxis_dual_left_properties.raw = 0;
1010 shared_memory->sixaxis_dual_right_properties.raw = 0;
1011 shared_memory->sixaxis_left_properties.raw = 0;
1012 shared_memory->sixaxis_right_properties.raw = 0;
980 shared_memory->battery_level_dual = 0; 1013 shared_memory->battery_level_dual = 0;
981 shared_memory->battery_level_left = 0; 1014 shared_memory->battery_level_left = 0;
982 shared_memory->battery_level_right = 0; 1015 shared_memory->battery_level_right = 0;
@@ -997,346 +1030,268 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
997 controller.device->Disconnect(); 1030 controller.device->Disconnect();
998 SignalStyleSetChangedEvent(npad_id); 1031 SignalStyleSetChangedEvent(npad_id);
999 WriteEmptyEntry(shared_memory); 1032 WriteEmptyEntry(shared_memory);
1033 return ResultSuccess;
1000} 1034}
1001 1035ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(
1002ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1036 const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
1003 GyroscopeZeroDriftMode drift_mode) { 1037 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1004 if (!IsDeviceHandleValid(sixaxis_handle)) { 1038 if (is_valid.IsError()) {
1005 LOG_ERROR(Service_HID, "Invalid handle"); 1039 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1006 return NpadInvalidHandle; 1040 return is_valid;
1007 } 1041 }
1008 1042
1009 auto& controller = GetControllerFromHandle(sixaxis_handle); 1043 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1010 switch (sixaxis_handle.npad_type) { 1044 sixaxis.gyroscope_zero_drift_mode = drift_mode;
1011 case Core::HID::NpadStyleIndex::ProController:
1012 controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
1013 break;
1014 case Core::HID::NpadStyleIndex::Handheld:
1015 controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
1016 break;
1017 case Core::HID::NpadStyleIndex::JoyconDual:
1018 case Core::HID::NpadStyleIndex::GameCube:
1019 case Core::HID::NpadStyleIndex::Pokeball:
1020 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1021 controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
1022 break;
1023 }
1024 controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
1025 break;
1026 case Core::HID::NpadStyleIndex::JoyconLeft:
1027 controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
1028 break;
1029 case Core::HID::NpadStyleIndex::JoyconRight:
1030 controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
1031 break;
1032 default:
1033 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1034 return NpadInvalidHandle;
1035 }
1036 1045
1037 return ResultSuccess; 1046 return ResultSuccess;
1038} 1047}
1039 1048
1040ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1049ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(
1041 GyroscopeZeroDriftMode& drift_mode) const { 1050 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1042 if (!IsDeviceHandleValid(sixaxis_handle)) { 1051 GyroscopeZeroDriftMode& drift_mode) const {
1043 LOG_ERROR(Service_HID, "Invalid handle"); 1052 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1044 return NpadInvalidHandle; 1053 if (is_valid.IsError()) {
1054 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1055 return is_valid;
1045 } 1056 }
1046 1057
1047 auto& controller = GetControllerFromHandle(sixaxis_handle); 1058 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1048 switch (sixaxis_handle.npad_type) { 1059 drift_mode = sixaxis.gyroscope_zero_drift_mode;
1049 case Core::HID::NpadStyleIndex::ProController:
1050 drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
1051 break;
1052 case Core::HID::NpadStyleIndex::Handheld:
1053 drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
1054 break;
1055 case Core::HID::NpadStyleIndex::JoyconDual:
1056 case Core::HID::NpadStyleIndex::GameCube:
1057 case Core::HID::NpadStyleIndex::Pokeball:
1058 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1059 drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
1060 break;
1061 }
1062 drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
1063 break;
1064 case Core::HID::NpadStyleIndex::JoyconLeft:
1065 drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
1066 break;
1067 case Core::HID::NpadStyleIndex::JoyconRight:
1068 drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
1069 break;
1070 default:
1071 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1072 return NpadInvalidHandle;
1073 }
1074 1060
1075 return ResultSuccess; 1061 return ResultSuccess;
1076} 1062}
1077 1063
1078ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, 1064ResultCode Controller_NPad::IsSixAxisSensorAtRest(
1079 bool& is_at_rest) const { 1065 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_at_rest) const {
1080 if (!IsDeviceHandleValid(sixaxis_handle)) { 1066 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1081 LOG_ERROR(Service_HID, "Invalid handle"); 1067 if (is_valid.IsError()) {
1082 return NpadInvalidHandle; 1068 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1069 return is_valid;
1083 } 1070 }
1071
1084 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1072 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1085 is_at_rest = controller.sixaxis_at_rest; 1073 is_at_rest = controller.sixaxis_at_rest;
1086 return ResultSuccess; 1074 return ResultSuccess;
1087} 1075}
1088 1076
1089ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor( 1077ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1090 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const { 1078 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
1091 if (!IsDeviceHandleValid(sixaxis_handle)) { 1079 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1092 LOG_ERROR(Service_HID, "Invalid handle"); 1080 if (is_valid.IsError()) {
1093 return NpadInvalidHandle; 1081 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1082 return is_valid;
1083 }
1084
1085 const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
1086 is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
1087 return ResultSuccess;
1088}
1089
1090ResultCode Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
1091 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
1092 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1093 if (is_valid.IsError()) {
1094 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1095 return is_valid;
1096 }
1097
1098 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1099 sixaxis.unaltered_passtrough = is_enabled;
1100 return ResultSuccess;
1101}
1102
1103ResultCode Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
1104 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
1105 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1106 if (is_valid.IsError()) {
1107 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1108 return is_valid;
1109 }
1110
1111 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1112 is_enabled = sixaxis.unaltered_passtrough;
1113 return ResultSuccess;
1114}
1115
1116ResultCode Controller_NPad::LoadSixAxisSensorCalibrationParameter(
1117 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1118 Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
1119 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1120 if (is_valid.IsError()) {
1121 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1122 return is_valid;
1094 } 1123 }
1095 1124
1096 // We don't support joycon firmware updates 1125 // TODO: Request this data to the controller. On error return 0xd8ca
1097 is_firmware_available = false; 1126 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1127 calibration = sixaxis.calibration;
1098 return ResultSuccess; 1128 return ResultSuccess;
1099} 1129}
1100 1130
1101ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1131ResultCode Controller_NPad::GetSixAxisSensorIcInformation(
1132 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1133 Core::HID::SixAxisSensorIcInformation& ic_information) const {
1134 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1135 if (is_valid.IsError()) {
1136 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1137 return is_valid;
1138 }
1139
1140 // TODO: Request this data to the controller. On error return 0xd8ca
1141 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1142 ic_information = sixaxis.ic_information;
1143 return ResultSuccess;
1144}
1145
1146ResultCode Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
1147 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1148 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1149 if (is_valid.IsError()) {
1150 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1151 return is_valid;
1152 }
1153
1154 auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
1155 sixaxis_properties.is_newly_assigned.Assign(0);
1156
1157 return ResultSuccess;
1158}
1159
1160ResultCode Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1102 bool sixaxis_status) { 1161 bool sixaxis_status) {
1103 if (!IsDeviceHandleValid(sixaxis_handle)) { 1162 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1104 LOG_ERROR(Service_HID, "Invalid handle"); 1163 if (is_valid.IsError()) {
1105 return NpadInvalidHandle; 1164 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1165 return is_valid;
1106 } 1166 }
1167
1107 auto& controller = GetControllerFromHandle(sixaxis_handle); 1168 auto& controller = GetControllerFromHandle(sixaxis_handle);
1108 controller.sixaxis_sensor_enabled = sixaxis_status; 1169 controller.sixaxis_sensor_enabled = sixaxis_status;
1109 return ResultSuccess; 1170 return ResultSuccess;
1110} 1171}
1111 1172
1112ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled( 1173ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
1113 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const { 1174 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
1114 if (!IsDeviceHandleValid(sixaxis_handle)) { 1175 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1115 LOG_ERROR(Service_HID, "Invalid handle"); 1176 if (is_valid.IsError()) {
1116 return NpadInvalidHandle; 1177 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1178 return is_valid;
1117 } 1179 }
1118 1180
1119 auto& controller = GetControllerFromHandle(sixaxis_handle); 1181 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1120 switch (sixaxis_handle.npad_type) { 1182 is_fusion_enabled = sixaxis.is_fusion_enabled;
1121 case Core::HID::NpadStyleIndex::ProController:
1122 is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
1123 break;
1124 case Core::HID::NpadStyleIndex::Handheld:
1125 is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
1126 break;
1127 case Core::HID::NpadStyleIndex::JoyconDual:
1128 case Core::HID::NpadStyleIndex::GameCube:
1129 case Core::HID::NpadStyleIndex::Pokeball:
1130 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1131 is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
1132 break;
1133 }
1134 is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
1135 break;
1136 case Core::HID::NpadStyleIndex::JoyconLeft:
1137 is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
1138 break;
1139 case Core::HID::NpadStyleIndex::JoyconRight:
1140 is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
1141 break;
1142 default:
1143 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1144 return NpadInvalidHandle;
1145 }
1146 1183
1147 return ResultSuccess; 1184 return ResultSuccess;
1148} 1185}
1149ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1186ResultCode Controller_NPad::SetSixAxisFusionEnabled(
1150 bool is_fusion_enabled) { 1187 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
1151 if (!IsDeviceHandleValid(sixaxis_handle)) { 1188 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1152 LOG_ERROR(Service_HID, "Invalid handle"); 1189 if (is_valid.IsError()) {
1153 return NpadInvalidHandle; 1190 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1191 return is_valid;
1154 } 1192 }
1155 1193
1156 auto& controller = GetControllerFromHandle(sixaxis_handle); 1194 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1157 switch (sixaxis_handle.npad_type) { 1195 sixaxis.is_fusion_enabled = is_fusion_enabled;
1158 case Core::HID::NpadStyleIndex::ProController:
1159 controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
1160 break;
1161 case Core::HID::NpadStyleIndex::Handheld:
1162 controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
1163 break;
1164 case Core::HID::NpadStyleIndex::JoyconDual:
1165 case Core::HID::NpadStyleIndex::GameCube:
1166 case Core::HID::NpadStyleIndex::Pokeball:
1167 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1168 controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
1169 break;
1170 }
1171 controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
1172 break;
1173 case Core::HID::NpadStyleIndex::JoyconLeft:
1174 controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
1175 break;
1176 case Core::HID::NpadStyleIndex::JoyconRight:
1177 controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
1178 break;
1179 default:
1180 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1181 return NpadInvalidHandle;
1182 }
1183 1196
1184 return ResultSuccess; 1197 return ResultSuccess;
1185} 1198}
1186 1199
1187ResultCode Controller_NPad::SetSixAxisFusionParameters( 1200ResultCode Controller_NPad::SetSixAxisFusionParameters(
1188 Core::HID::SixAxisSensorHandle sixaxis_handle, 1201 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1189 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { 1202 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1190 if (!IsDeviceHandleValid(sixaxis_handle)) { 1203 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1191 LOG_ERROR(Service_HID, "Invalid handle"); 1204 if (is_valid.IsError()) {
1192 return NpadInvalidHandle; 1205 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1206 return is_valid;
1193 } 1207 }
1208
1194 const auto param1 = sixaxis_fusion_parameters.parameter1; 1209 const auto param1 = sixaxis_fusion_parameters.parameter1;
1195 if (param1 < 0.0f || param1 > 1.0f) { 1210 if (param1 < 0.0f || param1 > 1.0f) {
1196 return InvalidSixAxisFusionRange; 1211 return InvalidSixAxisFusionRange;
1197 } 1212 }
1198 1213
1199 auto& controller = GetControllerFromHandle(sixaxis_handle); 1214 auto& sixaxis = GetSixaxisState(sixaxis_handle);
1200 switch (sixaxis_handle.npad_type) { 1215 sixaxis.fusion = sixaxis_fusion_parameters;
1201 case Core::HID::NpadStyleIndex::ProController:
1202 controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
1203 break;
1204 case Core::HID::NpadStyleIndex::Handheld:
1205 controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
1206 break;
1207 case Core::HID::NpadStyleIndex::JoyconDual:
1208 case Core::HID::NpadStyleIndex::GameCube:
1209 case Core::HID::NpadStyleIndex::Pokeball:
1210 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1211 controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
1212 break;
1213 }
1214 controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
1215 break;
1216 case Core::HID::NpadStyleIndex::JoyconLeft:
1217 controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
1218 break;
1219 case Core::HID::NpadStyleIndex::JoyconRight:
1220 controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
1221 break;
1222 default:
1223 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1224 return NpadInvalidHandle;
1225 }
1226 1216
1227 return ResultSuccess; 1217 return ResultSuccess;
1228} 1218}
1229 1219
1230ResultCode Controller_NPad::GetSixAxisFusionParameters( 1220ResultCode Controller_NPad::GetSixAxisFusionParameters(
1231 Core::HID::SixAxisSensorHandle sixaxis_handle, 1221 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
1232 Core::HID::SixAxisSensorFusionParameters& parameters) const { 1222 Core::HID::SixAxisSensorFusionParameters& parameters) const {
1233 if (!IsDeviceHandleValid(sixaxis_handle)) { 1223 const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
1234 LOG_ERROR(Service_HID, "Invalid handle"); 1224 if (is_valid.IsError()) {
1235 return NpadInvalidHandle; 1225 LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
1226 return is_valid;
1236 } 1227 }
1237 1228
1238 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1229 const auto& sixaxis = GetSixaxisState(sixaxis_handle);
1239 switch (sixaxis_handle.npad_type) { 1230 parameters = sixaxis.fusion;
1240 case Core::HID::NpadStyleIndex::ProController:
1241 parameters = controller.sixaxis_fullkey.fusion;
1242 break;
1243 case Core::HID::NpadStyleIndex::Handheld:
1244 parameters = controller.sixaxis_handheld.fusion;
1245 break;
1246 case Core::HID::NpadStyleIndex::JoyconDual:
1247 case Core::HID::NpadStyleIndex::GameCube:
1248 case Core::HID::NpadStyleIndex::Pokeball:
1249 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1250 parameters = controller.sixaxis_dual_left.fusion;
1251 break;
1252 }
1253 parameters = controller.sixaxis_dual_right.fusion;
1254 break;
1255 case Core::HID::NpadStyleIndex::JoyconLeft:
1256 parameters = controller.sixaxis_left.fusion;
1257 break;
1258 case Core::HID::NpadStyleIndex::JoyconRight:
1259 parameters = controller.sixaxis_right.fusion;
1260 break;
1261 default:
1262 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1263 return NpadInvalidHandle;
1264 }
1265 1231
1266 return ResultSuccess; 1232 return ResultSuccess;
1267} 1233}
1268 1234
1269void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1235ResultCode Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
1270 Core::HID::NpadIdType npad_id_2) { 1236 Core::HID::NpadIdType npad_id_2) {
1271 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1237 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1272 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1238 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1273 npad_id_2); 1239 npad_id_2);
1274 return; 1240 return InvalidNpadId;
1275 } 1241 }
1276 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1); 1242 auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
1277 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2); 1243 auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
1278 const auto controller_style_1 = controller_1.device->GetNpadStyleIndex(); 1244 auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
1279 const auto controller_style_2 = controller_2.device->GetNpadStyleIndex(); 1245 auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
1280 bool merge_controllers = false;
1281 1246
1282 // If the controllers at both npad indices form a pair of left and right joycons, merge them. 1247 // Simplify this code by converting dualjoycon with only a side connected to single joycons
1283 // Otherwise, do nothing. 1248 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
1249 if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
1250 controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
1251 }
1252 if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1253 controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
1254 }
1255 }
1256 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
1257 if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1258 controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
1259 }
1260 if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1261 controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
1262 }
1263 }
1264
1265 // Invalid merge errors
1266 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
1267 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
1268 return NpadIsDualJoycon;
1269 }
1284 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft && 1270 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1271 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
1272 return NpadIsSameType;
1273 }
1274 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1285 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) { 1275 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
1286 merge_controllers = true; 1276 return NpadIsSameType;
1287 } 1277 }
1288 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft && 1278
1289 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) { 1279 // These exceptions are handled as if they where dual joycon
1290 merge_controllers = true; 1280 if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
1291 } 1281 controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
1292 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual && 1282 return NpadIsDualJoycon;
1293 controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
1294 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
1295 merge_controllers = true;
1296 }
1297 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1298 controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
1299 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
1300 merge_controllers = true;
1301 }
1302 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1303 controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
1304 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1305 merge_controllers = true;
1306 }
1307 if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1308 controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
1309 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1310 merge_controllers = true;
1311 }
1312 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1313 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1314 controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
1315 !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
1316 merge_controllers = true;
1317 }
1318 if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
1319 controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
1320 !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
1321 controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
1322 merge_controllers = true;
1323 }
1324
1325 if (merge_controllers) {
1326 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1327 DisconnectNpad(npad_id_2);
1328 controller_1.is_dual_left_connected = true;
1329 controller_1.is_dual_right_connected = true;
1330 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1331 return;
1332 } 1283 }
1333 LOG_WARNING(Service_HID, 1284 if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
1334 "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, " 1285 controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
1335 "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}", 1286 return NpadIsDualJoycon;
1336 npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(), 1287 }
1337 controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected, 1288
1338 controller_1.is_dual_right_connected, controller_2.is_dual_left_connected, 1289 // Disconnect the joycon at the second id and connect the dual joycon at the first index.
1339 controller_2.is_dual_right_connected); 1290 DisconnectNpad(npad_id_2);
1291 controller_1.is_dual_left_connected = true;
1292 controller_1.is_dual_right_connected = true;
1293 AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
1294 return ResultSuccess;
1340} 1295}
1341 1296
1342void Controller_NPad::StartLRAssignmentMode() { 1297void Controller_NPad::StartLRAssignmentMode() {
@@ -1349,17 +1304,17 @@ void Controller_NPad::StopLRAssignmentMode() {
1349 is_in_lr_assignment_mode = false; 1304 is_in_lr_assignment_mode = false;
1350} 1305}
1351 1306
1352bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, 1307ResultCode Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1353 Core::HID::NpadIdType npad_id_2) { 1308 Core::HID::NpadIdType npad_id_2) {
1354 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) { 1309 if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
1355 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1, 1310 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
1356 npad_id_2); 1311 npad_id_2);
1357 return false; 1312 return InvalidNpadId;
1358 } 1313 }
1359 if (npad_id_1 == Core::HID::NpadIdType::Handheld || 1314 if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
1360 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other || 1315 npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
1361 npad_id_2 == Core::HID::NpadIdType::Other) { 1316 npad_id_2 == Core::HID::NpadIdType::Other) {
1362 return true; 1317 return ResultSuccess;
1363 } 1318 }
1364 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device; 1319 const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
1365 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device; 1320 const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
@@ -1369,46 +1324,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
1369 const auto is_connected_2 = controller_2->IsConnected(); 1324 const auto is_connected_2 = controller_2->IsConnected();
1370 1325
1371 if (!IsControllerSupported(type_index_1) && is_connected_1) { 1326 if (!IsControllerSupported(type_index_1) && is_connected_1) {
1372 return false; 1327 return NpadNotConnected;
1373 } 1328 }
1374 if (!IsControllerSupported(type_index_2) && is_connected_2) { 1329 if (!IsControllerSupported(type_index_2) && is_connected_2) {
1375 return false; 1330 return NpadNotConnected;
1376 } 1331 }
1377 1332
1378 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2); 1333 UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
1379 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1); 1334 UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
1380 1335
1381 return true; 1336 return ResultSuccess;
1382} 1337}
1383 1338
1384Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) { 1339ResultCode Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
1340 Core::HID::LedPattern& pattern) const {
1385 if (!IsNpadIdValid(npad_id)) { 1341 if (!IsNpadIdValid(npad_id)) {
1386 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1342 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1387 return Core::HID::LedPattern{0, 0, 0, 0}; 1343 return InvalidNpadId;
1388 } 1344 }
1389 const auto& controller = GetControllerFromNpadIdType(npad_id).device; 1345 const auto& controller = GetControllerFromNpadIdType(npad_id).device;
1390 return controller->GetLedPattern(); 1346 pattern = controller->GetLedPattern();
1347 return ResultSuccess;
1391} 1348}
1392 1349
1393bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled( 1350ResultCode Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
1394 Core::HID::NpadIdType npad_id) const { 1351 Core::HID::NpadIdType npad_id, bool& is_valid) const {
1395 if (!IsNpadIdValid(npad_id)) { 1352 if (!IsNpadIdValid(npad_id)) {
1396 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1353 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1397 // Return the default value 1354 return InvalidNpadId;
1398 return false;
1399 } 1355 }
1400 const auto& controller = GetControllerFromNpadIdType(npad_id); 1356 const auto& controller = GetControllerFromNpadIdType(npad_id);
1401 return controller.unintended_home_button_input_protection; 1357 is_valid = controller.unintended_home_button_input_protection;
1358 return ResultSuccess;
1402} 1359}
1403 1360
1404void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 1361ResultCode Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
1405 Core::HID::NpadIdType npad_id) { 1362 bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
1406 if (!IsNpadIdValid(npad_id)) { 1363 if (!IsNpadIdValid(npad_id)) {
1407 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id); 1364 LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
1408 return; 1365 return InvalidNpadId;
1409 } 1366 }
1410 auto& controller = GetControllerFromNpadIdType(npad_id); 1367 auto& controller = GetControllerFromNpadIdType(npad_id);
1411 controller.unintended_home_button_input_protection = is_protection_enabled; 1368 controller.unintended_home_button_input_protection = is_protection_enabled;
1369 return ResultSuccess;
1412} 1370}
1413 1371
1414void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) { 1372void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1546,4 +1504,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
1546 return controller_data[npad_index]; 1504 return controller_data[npad_index];
1547} 1505}
1548 1506
1507Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1508 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1509 auto& controller = GetControllerFromHandle(sixaxis_handle);
1510 switch (sixaxis_handle.npad_type) {
1511 case Core::HID::NpadStyleIndex::ProController:
1512 case Core::HID::NpadStyleIndex::Pokeball:
1513 return controller.shared_memory->sixaxis_fullkey_properties;
1514 case Core::HID::NpadStyleIndex::Handheld:
1515 return controller.shared_memory->sixaxis_handheld_properties;
1516 case Core::HID::NpadStyleIndex::JoyconDual:
1517 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1518 return controller.shared_memory->sixaxis_dual_left_properties;
1519 }
1520 return controller.shared_memory->sixaxis_dual_right_properties;
1521 case Core::HID::NpadStyleIndex::JoyconLeft:
1522 return controller.shared_memory->sixaxis_left_properties;
1523 case Core::HID::NpadStyleIndex::JoyconRight:
1524 return controller.shared_memory->sixaxis_right_properties;
1525 default:
1526 return controller.shared_memory->sixaxis_fullkey_properties;
1527 }
1528}
1529
1530const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
1531 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1532 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1533 switch (sixaxis_handle.npad_type) {
1534 case Core::HID::NpadStyleIndex::ProController:
1535 case Core::HID::NpadStyleIndex::Pokeball:
1536 return controller.shared_memory->sixaxis_fullkey_properties;
1537 case Core::HID::NpadStyleIndex::Handheld:
1538 return controller.shared_memory->sixaxis_handheld_properties;
1539 case Core::HID::NpadStyleIndex::JoyconDual:
1540 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1541 return controller.shared_memory->sixaxis_dual_left_properties;
1542 }
1543 return controller.shared_memory->sixaxis_dual_right_properties;
1544 case Core::HID::NpadStyleIndex::JoyconLeft:
1545 return controller.shared_memory->sixaxis_left_properties;
1546 case Core::HID::NpadStyleIndex::JoyconRight:
1547 return controller.shared_memory->sixaxis_right_properties;
1548 default:
1549 return controller.shared_memory->sixaxis_fullkey_properties;
1550 }
1551}
1552
1553Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
1554 const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
1555 auto& controller = GetControllerFromHandle(sixaxis_handle);
1556 switch (sixaxis_handle.npad_type) {
1557 case Core::HID::NpadStyleIndex::ProController:
1558 case Core::HID::NpadStyleIndex::Pokeball:
1559 return controller.sixaxis_fullkey;
1560 case Core::HID::NpadStyleIndex::Handheld:
1561 return controller.sixaxis_handheld;
1562 case Core::HID::NpadStyleIndex::JoyconDual:
1563 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1564 return controller.sixaxis_dual_left;
1565 }
1566 return controller.sixaxis_dual_right;
1567 case Core::HID::NpadStyleIndex::JoyconLeft:
1568 return controller.sixaxis_left;
1569 case Core::HID::NpadStyleIndex::JoyconRight:
1570 return controller.sixaxis_right;
1571 default:
1572 return controller.sixaxis_unknown;
1573 }
1574}
1575
1576const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
1577 const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
1578 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1579 switch (sixaxis_handle.npad_type) {
1580 case Core::HID::NpadStyleIndex::ProController:
1581 case Core::HID::NpadStyleIndex::Pokeball:
1582 return controller.sixaxis_fullkey;
1583 case Core::HID::NpadStyleIndex::Handheld:
1584 return controller.sixaxis_handheld;
1585 case Core::HID::NpadStyleIndex::JoyconDual:
1586 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1587 return controller.sixaxis_dual_left;
1588 }
1589 return controller.sixaxis_dual_right;
1590 case Core::HID::NpadStyleIndex::JoyconLeft:
1591 return controller.sixaxis_left;
1592 case Core::HID::NpadStyleIndex::JoyconRight:
1593 return controller.sixaxis_right;
1594 default:
1595 return controller.sixaxis_unknown;
1596 }
1597}
1598
1549} // namespace Service::HID 1599} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 0a96825a5..0b662b7f8 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -107,8 +107,8 @@ public:
107 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_); 107 void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
108 NpadCommunicationMode GetNpadCommunicationMode() const; 108 NpadCommunicationMode GetNpadCommunicationMode() const;
109 109
110 void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type, 110 ResultCode SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
111 NpadJoyAssignmentMode assignment_mode); 111 NpadJoyAssignmentMode assignment_mode);
112 112
113 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index, 113 bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
114 const Core::HID::VibrationValue& vibration_value); 114 const Core::HID::VibrationValue& vibration_value);
@@ -141,50 +141,65 @@ public:
141 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id, 141 void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
142 bool connected); 142 bool connected);
143 143
144 void DisconnectNpad(Core::HID::NpadIdType npad_id); 144 ResultCode DisconnectNpad(Core::HID::NpadIdType npad_id);
145 145
146 ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 146 ResultCode SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
147 GyroscopeZeroDriftMode drift_mode); 147 GyroscopeZeroDriftMode drift_mode);
148 ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 148 ResultCode GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
149 GyroscopeZeroDriftMode& drift_mode) const; 149 GyroscopeZeroDriftMode& drift_mode) const;
150 ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle, 150 ResultCode IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
151 bool& is_at_rest) const; 151 bool& is_at_rest) const;
152 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor( 152 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
153 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const; 153 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
154 ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 154 ResultCode EnableSixAxisSensorUnalteredPassthrough(
155 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
156 ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(
157 const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
158 ResultCode LoadSixAxisSensorCalibrationParameter(
159 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
160 Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
161 ResultCode GetSixAxisSensorIcInformation(
162 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
163 Core::HID::SixAxisSensorIcInformation& ic_information) const;
164 ResultCode ResetIsSixAxisSensorDeviceNewlyAssigned(
165 const Core::HID::SixAxisSensorHandle& sixaxis_handle);
166 ResultCode SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
155 bool sixaxis_status); 167 bool sixaxis_status);
156 ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 168 ResultCode IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
157 bool& is_fusion_enabled) const; 169 bool& is_fusion_enabled) const;
158 ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 170 ResultCode SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
159 bool is_fusion_enabled); 171 bool is_fusion_enabled);
160 ResultCode SetSixAxisFusionParameters( 172 ResultCode SetSixAxisFusionParameters(
161 Core::HID::SixAxisSensorHandle sixaxis_handle, 173 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
162 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); 174 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
163 ResultCode GetSixAxisFusionParameters( 175 ResultCode GetSixAxisFusionParameters(
164 Core::HID::SixAxisSensorHandle sixaxis_handle, 176 const Core::HID::SixAxisSensorHandle& sixaxis_handle,
165 Core::HID::SixAxisSensorFusionParameters& parameters) const; 177 Core::HID::SixAxisSensorFusionParameters& parameters) const;
166 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); 178 ResultCode GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
167 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; 179 ResultCode IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
168 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 180 bool& is_enabled) const;
169 Core::HID::NpadIdType npad_id); 181 ResultCode SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
182 Core::HID::NpadIdType npad_id);
170 void SetAnalogStickUseCenterClamp(bool use_center_clamp); 183 void SetAnalogStickUseCenterClamp(bool use_center_clamp);
171 void ClearAllConnectedControllers(); 184 void ClearAllConnectedControllers();
172 void DisconnectAllConnectedControllers(); 185 void DisconnectAllConnectedControllers();
173 void ConnectAllDisconnectedControllers(); 186 void ConnectAllDisconnectedControllers();
174 void ClearAllControllers(); 187 void ClearAllControllers();
175 188
176 void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); 189 ResultCode MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
190 Core::HID::NpadIdType npad_id_2);
177 void StartLRAssignmentMode(); 191 void StartLRAssignmentMode();
178 void StopLRAssignmentMode(); 192 void StopLRAssignmentMode();
179 bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2); 193 ResultCode SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
180 194
181 // Logical OR for all buttons presses on all controllers 195 // Logical OR for all buttons presses on all controllers
182 // Specifically for cheat engine and other features. 196 // Specifically for cheat engine and other features.
183 Core::HID::NpadButton GetAndResetPressState(); 197 Core::HID::NpadButton GetAndResetPressState();
184 198
185 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id); 199 static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
186 static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
187 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); 200 static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
201 static ResultCode VerifyValidSixAxisSensorHandle(
202 const Core::HID::SixAxisSensorHandle& device_handle);
188 203
189private: 204private:
190 static constexpr std::size_t NPAD_COUNT = 10; 205 static constexpr std::size_t NPAD_COUNT = 10;
@@ -451,9 +466,13 @@ private:
451 NpadLuciaType lucia_type{}; 466 NpadLuciaType lucia_type{};
452 NpadLagonType lagon_type{}; 467 NpadLagonType lagon_type{};
453 NpadLagerType lager_type{}; 468 NpadLagerType lager_type{};
454 // FW 13.x Investigate there is some sort of bitflag related to joycons 469 Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
455 INSERT_PADDING_BYTES(0x4); 470 Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
456 INSERT_PADDING_BYTES(0xc08); // Unknown 471 Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
472 Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
473 Core::HID::SixAxisSensorProperties sixaxis_left_properties;
474 Core::HID::SixAxisSensorProperties sixaxis_right_properties;
475 INSERT_PADDING_BYTES(0xc06); // Unknown
457 }; 476 };
458 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size"); 477 static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
459 478
@@ -465,7 +484,10 @@ private:
465 484
466 struct SixaxisParameters { 485 struct SixaxisParameters {
467 bool is_fusion_enabled{true}; 486 bool is_fusion_enabled{true};
487 bool unaltered_passtrough{false};
468 Core::HID::SixAxisSensorFusionParameters fusion{}; 488 Core::HID::SixAxisSensorFusionParameters fusion{};
489 Core::HID::SixAxisSensorCalibrationParameter calibration{};
490 Core::HID::SixAxisSensorIcInformation ic_information{};
469 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 491 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
470 }; 492 };
471 493
@@ -491,6 +513,7 @@ private:
491 SixaxisParameters sixaxis_dual_right{}; 513 SixaxisParameters sixaxis_dual_right{};
492 SixaxisParameters sixaxis_left{}; 514 SixaxisParameters sixaxis_left{};
493 SixaxisParameters sixaxis_right{}; 515 SixaxisParameters sixaxis_right{};
516 SixaxisParameters sixaxis_unknown{};
494 517
495 // Current pad state 518 // Current pad state
496 NPadGenericState npad_pad_state{}; 519 NPadGenericState npad_pad_state{};
@@ -522,6 +545,14 @@ private:
522 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id); 545 NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
523 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const; 546 const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
524 547
548 Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
549 const Core::HID::SixAxisSensorHandle& device_handle);
550 const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
551 const Core::HID::SixAxisSensorHandle& device_handle) const;
552 SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
553 const SixaxisParameters& GetSixaxisState(
554 const Core::HID::SixAxisSensorHandle& device_handle) const;
555
525 std::atomic<u64> press_state{}; 556 std::atomic<u64> press_state{};
526 557
527 std::array<NpadControllerData, NPAD_COUNT> controller_data{}; 558 std::array<NpadControllerData, NPAD_COUNT> controller_data{};
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index b31834074..6c8ad04af 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -8,7 +8,11 @@
8namespace Service::HID { 8namespace Service::HID {
9 9
10constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100}; 10constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
11constexpr ResultCode NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
11constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423}; 12constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr ResultCode NpadIsDualJoycon{ErrorModule::HID, 601};
14constexpr ResultCode NpadIsSameType{ErrorModule::HID, 602};
15constexpr ResultCode InvalidNpadId{ErrorModule::HID, 709};
12constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710}; 16constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
13 17
14} // namespace Service::HID 18} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 44f892da9..8a496c38c 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -257,12 +257,12 @@ Hid::Hid(Core::System& system_)
257 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, 257 {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
258 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, 258 {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
259 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, 259 {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
260 {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"}, 260 {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
261 {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"}, 261 {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
262 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, 262 {86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
263 {87, nullptr, "LoadSixAxisSensorCalibrationParameter"}, 263 {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
264 {88, nullptr, "GetSixAxisSensorIcInformation"}, 264 {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
265 {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, 265 {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
266 {91, &Hid::ActivateGesture, "ActivateGesture"}, 266 {91, &Hid::ActivateGesture, "ActivateGesture"},
267 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, 267 {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
268 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, 268 {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -694,11 +694,7 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
694 rb.Push(result1); 694 rb.Push(result1);
695 return; 695 return;
696 } 696 }
697 if (result2.IsError()) { 697 rb.Push(result2);
698 rb.Push(result2);
699 return;
700 }
701 rb.Push(ResultSuccess);
702} 698}
703 699
704void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 700void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -821,6 +817,144 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
821 rb.Push(is_firmware_available); 817 rb.Push(is_firmware_available);
822} 818}
823 819
820void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
821 IPC::RequestParser rp{ctx};
822 struct Parameters {
823 bool enabled;
824 Core::HID::SixAxisSensorHandle sixaxis_handle;
825 u64 applet_resource_user_id;
826 };
827 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
828
829 const auto parameters{rp.PopRaw<Parameters>()};
830
831 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
832 const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
833 parameters.sixaxis_handle, parameters.enabled);
834
835 LOG_WARNING(Service_HID,
836 "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
837 "applet_resource_user_id={}",
838 parameters.enabled, parameters.sixaxis_handle.npad_type,
839 parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
840 parameters.applet_resource_user_id);
841
842 IPC::ResponseBuilder rb{ctx, 2};
843 rb.Push(result);
844}
845
846void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
847 IPC::RequestParser rp{ctx};
848 struct Parameters {
849 Core::HID::SixAxisSensorHandle sixaxis_handle;
850 INSERT_PADDING_WORDS_NOINIT(1);
851 u64 applet_resource_user_id;
852 };
853 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
854
855 const auto parameters{rp.PopRaw<Parameters>()};
856
857 bool is_unaltered_sisxaxis_enabled{};
858 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
859 const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
860 parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
861
862 LOG_WARNING(
863 Service_HID,
864 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
865 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
866 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
867
868 IPC::ResponseBuilder rb{ctx, 3};
869 rb.Push(result);
870 rb.Push(is_unaltered_sisxaxis_enabled);
871}
872
873void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
874 IPC::RequestParser rp{ctx};
875 struct Parameters {
876 Core::HID::SixAxisSensorHandle sixaxis_handle;
877 INSERT_PADDING_WORDS_NOINIT(1);
878 u64 applet_resource_user_id;
879 };
880 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
881
882 const auto parameters{rp.PopRaw<Parameters>()};
883
884 Core::HID::SixAxisSensorCalibrationParameter calibration{};
885 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
886 const auto result =
887 controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
888
889 LOG_WARNING(
890 Service_HID,
891 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
892 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
893 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
894
895 if (result.IsSuccess()) {
896 ctx.WriteBuffer(calibration);
897 }
898
899 IPC::ResponseBuilder rb{ctx, 2};
900 rb.Push(result);
901}
902
903void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
904 IPC::RequestParser rp{ctx};
905 struct Parameters {
906 Core::HID::SixAxisSensorHandle sixaxis_handle;
907 INSERT_PADDING_WORDS_NOINIT(1);
908 u64 applet_resource_user_id;
909 };
910 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
911
912 const auto parameters{rp.PopRaw<Parameters>()};
913
914 Core::HID::SixAxisSensorIcInformation ic_information{};
915 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
916 const auto result =
917 controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
918
919 LOG_WARNING(
920 Service_HID,
921 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
922 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
923 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
924
925 if (result.IsSuccess()) {
926 ctx.WriteBuffer(ic_information);
927 }
928
929 IPC::ResponseBuilder rb{ctx, 2};
930 rb.Push(result);
931}
932
933void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
934 IPC::RequestParser rp{ctx};
935 struct Parameters {
936 Core::HID::SixAxisSensorHandle sixaxis_handle;
937 INSERT_PADDING_WORDS_NOINIT(1);
938 u64 applet_resource_user_id;
939 };
940 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
941
942 const auto parameters{rp.PopRaw<Parameters>()};
943
944 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
945 const auto result =
946 controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
947
948 LOG_WARNING(
949 Service_HID,
950 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
951 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
952 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
953
954 IPC::ResponseBuilder rb{ctx, 2};
955 rb.Push(result);
956}
957
824void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 958void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
825 IPC::RequestParser rp{ctx}; 959 IPC::RequestParser rp{ctx};
826 struct Parameters { 960 struct Parameters {
@@ -948,27 +1082,29 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
948 1082
949 const auto parameters{rp.PopRaw<Parameters>()}; 1083 const auto parameters{rp.PopRaw<Parameters>()};
950 1084
951 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1085 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
952 .DisconnectNpad(parameters.npad_id); 1086 const auto result = controller.DisconnectNpad(parameters.npad_id);
953 1087
954 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1088 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
955 parameters.applet_resource_user_id); 1089 parameters.applet_resource_user_id);
956 1090
957 IPC::ResponseBuilder rb{ctx, 2}; 1091 IPC::ResponseBuilder rb{ctx, 2};
958 rb.Push(ResultSuccess); 1092 rb.Push(result);
959} 1093}
960 1094
961void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) { 1095void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
962 IPC::RequestParser rp{ctx}; 1096 IPC::RequestParser rp{ctx};
963 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; 1097 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
964 1098
1099 Core::HID::LedPattern pattern{0, 0, 0, 0};
1100 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1101 const auto result = controller.GetLedPattern(npad_id, pattern);
1102
965 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); 1103 LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
966 1104
967 IPC::ResponseBuilder rb{ctx, 4}; 1105 IPC::ResponseBuilder rb{ctx, 4};
968 rb.Push(ResultSuccess); 1106 rb.Push(result);
969 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 1107 rb.Push(pattern.raw);
970 .GetLedPattern(npad_id)
971 .raw);
972} 1108}
973 1109
974void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { 1110void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
@@ -1028,15 +1164,16 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
1028 1164
1029 const auto parameters{rp.PopRaw<Parameters>()}; 1165 const auto parameters{rp.PopRaw<Parameters>()};
1030 1166
1031 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1167 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1032 .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left, 1168 const auto result =
1033 Controller_NPad::NpadJoyAssignmentMode::Single); 1169 controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
1170 Controller_NPad::NpadJoyAssignmentMode::Single);
1034 1171
1035 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1172 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1036 parameters.applet_resource_user_id); 1173 parameters.applet_resource_user_id);
1037 1174
1038 IPC::ResponseBuilder rb{ctx, 2}; 1175 IPC::ResponseBuilder rb{ctx, 2};
1039 rb.Push(ResultSuccess); 1176 rb.Push(result);
1040} 1177}
1041 1178
1042void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) { 1179void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
@@ -1051,16 +1188,16 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
1051 1188
1052 const auto parameters{rp.PopRaw<Parameters>()}; 1189 const auto parameters{rp.PopRaw<Parameters>()};
1053 1190
1054 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1191 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1055 .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type, 1192 const auto result = controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
1056 Controller_NPad::NpadJoyAssignmentMode::Single); 1193 Controller_NPad::NpadJoyAssignmentMode::Single);
1057 1194
1058 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", 1195 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
1059 parameters.npad_id, parameters.applet_resource_user_id, 1196 parameters.npad_id, parameters.applet_resource_user_id,
1060 parameters.npad_joy_device_type); 1197 parameters.npad_joy_device_type);
1061 1198
1062 IPC::ResponseBuilder rb{ctx, 2}; 1199 IPC::ResponseBuilder rb{ctx, 2};
1063 rb.Push(ResultSuccess); 1200 rb.Push(result);
1064} 1201}
1065 1202
1066void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { 1203void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
@@ -1074,14 +1211,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
1074 1211
1075 const auto parameters{rp.PopRaw<Parameters>()}; 1212 const auto parameters{rp.PopRaw<Parameters>()};
1076 1213
1077 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1214 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1078 .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); 1215 const auto result = controller.SetNpadMode(parameters.npad_id, {},
1216 Controller_NPad::NpadJoyAssignmentMode::Dual);
1079 1217
1080 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, 1218 LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
1081 parameters.applet_resource_user_id); 1219 parameters.applet_resource_user_id);
1082 1220
1083 IPC::ResponseBuilder rb{ctx, 2}; 1221 IPC::ResponseBuilder rb{ctx, 2};
1084 rb.Push(ResultSuccess); 1222 rb.Push(result);
1085} 1223}
1086 1224
1087void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { 1225void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
@@ -1090,14 +1228,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
1090 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1228 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1091 const auto applet_resource_user_id{rp.Pop<u64>()}; 1229 const auto applet_resource_user_id{rp.Pop<u64>()};
1092 1230
1093 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1231 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1094 .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); 1232 const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
1095 1233
1096 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1234 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1097 npad_id_1, npad_id_2, applet_resource_user_id); 1235 npad_id_1, npad_id_2, applet_resource_user_id);
1098 1236
1099 IPC::ResponseBuilder rb{ctx, 2}; 1237 IPC::ResponseBuilder rb{ctx, 2};
1100 rb.Push(ResultSuccess); 1238 rb.Push(result);
1101} 1239}
1102 1240
1103void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { 1241void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
@@ -1157,19 +1295,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1157 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()}; 1295 const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
1158 const auto applet_resource_user_id{rp.Pop<u64>()}; 1296 const auto applet_resource_user_id{rp.Pop<u64>()};
1159 1297
1160 const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad) 1298 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1161 .SwapNpadAssignment(npad_id_1, npad_id_2); 1299 const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
1162 1300
1163 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", 1301 LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
1164 npad_id_1, npad_id_2, applet_resource_user_id); 1302 npad_id_1, npad_id_2, applet_resource_user_id);
1165 1303
1166 IPC::ResponseBuilder rb{ctx, 2}; 1304 IPC::ResponseBuilder rb{ctx, 2};
1167 if (res) { 1305 rb.Push(result);
1168 rb.Push(ResultSuccess);
1169 } else {
1170 LOG_ERROR(Service_HID, "Npads are not connected!");
1171 rb.Push(NpadNotConnected);
1172 }
1173} 1306}
1174 1307
1175void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { 1308void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
@@ -1183,13 +1316,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
1183 1316
1184 const auto parameters{rp.PopRaw<Parameters>()}; 1317 const auto parameters{rp.PopRaw<Parameters>()};
1185 1318
1319 bool is_enabled = false;
1320 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1321 const auto result =
1322 controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
1323
1186 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", 1324 LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
1187 parameters.npad_id, parameters.applet_resource_user_id); 1325 parameters.npad_id, parameters.applet_resource_user_id);
1188 1326
1189 IPC::ResponseBuilder rb{ctx, 3}; 1327 IPC::ResponseBuilder rb{ctx, 3};
1190 rb.Push(ResultSuccess); 1328 rb.Push(result);
1191 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 1329 rb.Push(is_enabled);
1192 .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
1193} 1330}
1194 1331
1195void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { 1332void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
@@ -1204,9 +1341,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1204 1341
1205 const auto parameters{rp.PopRaw<Parameters>()}; 1342 const auto parameters{rp.PopRaw<Parameters>()};
1206 1343
1207 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1344 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
1208 .SetUnintendedHomeButtonInputProtectionEnabled( 1345 const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
1209 parameters.unintended_home_button_input_protection, parameters.npad_id); 1346 parameters.unintended_home_button_input_protection, parameters.npad_id);
1210 1347
1211 LOG_WARNING(Service_HID, 1348 LOG_WARNING(Service_HID,
1212 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," 1349 "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
@@ -1215,7 +1352,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
1215 parameters.applet_resource_user_id); 1352 parameters.applet_resource_user_id);
1216 1353
1217 IPC::ResponseBuilder rb{ctx, 2}; 1354 IPC::ResponseBuilder rb{ctx, 2};
1218 rb.Push(ResultSuccess); 1355 rb.Push(result);
1219} 1356}
1220 1357
1221void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) { 1358void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
@@ -1377,6 +1514,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
1377 IPC::RequestParser rp{ctx}; 1514 IPC::RequestParser rp{ctx};
1378 const auto can_vibrate{rp.Pop<bool>()}; 1515 const auto can_vibrate{rp.Pop<bool>()};
1379 1516
1517 // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
1518 // by converting it to a bool
1380 Settings::values.vibration_enabled.SetValue(can_vibrate); 1519 Settings::values.vibration_enabled.SetValue(can_vibrate);
1381 1520
1382 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); 1521 LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -1388,9 +1527,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
1388void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { 1527void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
1389 LOG_DEBUG(Service_HID, "called"); 1528 LOG_DEBUG(Service_HID, "called");
1390 1529
1530 // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
1531 const auto is_enabled = Settings::values.vibration_enabled.GetValue();
1532
1391 IPC::ResponseBuilder rb{ctx, 3}; 1533 IPC::ResponseBuilder rb{ctx, 3};
1392 rb.Push(ResultSuccess); 1534 rb.Push(ResultSuccess);
1393 rb.Push(Settings::values.vibration_enabled.GetValue()); 1535 rb.Push(is_enabled);
1394} 1536}
1395 1537
1396void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { 1538void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 1be04c22b..ac4333022 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -113,6 +113,11 @@ private:
113 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); 113 void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
114 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx); 114 void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
115 void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx); 115 void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
116 void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
117 void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
118 void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
119 void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
120 void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
116 void ActivateGesture(Kernel::HLERequestContext& ctx); 121 void ActivateGesture(Kernel::HLERequestContext& ctx);
117 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 122 void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
118 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx); 123 void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 9e32f3e60..d2a91d913 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -5,7 +5,9 @@
5#include "core/core_timing.h" 5#include "core/core_timing.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_shared_memory.h" 7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/k_transfer_memory.h"
8#include "core/hle/kernel/kernel.h" 9#include "core/hle/kernel/kernel.h"
10#include "core/hle/service/hid/errors.h"
9#include "core/hle/service/hid/irs.h" 11#include "core/hle/service/hid/irs.h"
10 12
11namespace Service::HID { 13namespace Service::HID {
@@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
38} 40}
39 41
40void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { 42void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
41 LOG_WARNING(Service_IRS, "(STUBBED) called"); 43 IPC::RequestParser rp{ctx};
44 const auto applet_resource_user_id{rp.Pop<u64>()};
45
46 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
47 applet_resource_user_id);
42 48
43 IPC::ResponseBuilder rb{ctx, 2}; 49 IPC::ResponseBuilder rb{ctx, 2};
44 rb.Push(ResultSuccess); 50 rb.Push(ResultSuccess);
45} 51}
46 52
47void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { 53void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
48 LOG_WARNING(Service_IRS, "(STUBBED) called"); 54 IPC::RequestParser rp{ctx};
55 const auto applet_resource_user_id{rp.Pop<u64>()};
56
57 LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
58 applet_resource_user_id);
49 59
50 IPC::ResponseBuilder rb{ctx, 2}; 60 IPC::ResponseBuilder rb{ctx, 2};
51 rb.Push(ResultSuccess); 61 rb.Push(ResultSuccess);
52} 62}
53 63
54void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { 64void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
55 LOG_DEBUG(Service_IRS, "called"); 65 IPC::RequestParser rp{ctx};
66 const auto applet_resource_user_id{rp.Pop<u64>()};
67
68 LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
56 69
57 IPC::ResponseBuilder rb{ctx, 2, 1}; 70 IPC::ResponseBuilder rb{ctx, 2, 1};
58 rb.Push(ResultSuccess); 71 rb.Push(ResultSuccess);
@@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
60} 73}
61 74
62void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { 75void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
63 LOG_WARNING(Service_IRS, "(STUBBED) called"); 76 IPC::RequestParser rp{ctx};
77 struct Parameters {
78 IrCameraHandle camera_handle;
79 INSERT_PADDING_WORDS_NOINIT(1);
80 u64 applet_resource_user_id;
81 };
82 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
83
84 const auto parameters{rp.PopRaw<Parameters>()};
85
86 LOG_WARNING(Service_IRS,
87 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
88 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
89 parameters.applet_resource_user_id);
64 90
65 IPC::ResponseBuilder rb{ctx, 2}; 91 IPC::ResponseBuilder rb{ctx, 2};
66 rb.Push(ResultSuccess); 92 rb.Push(ResultSuccess);
67} 93}
68 94
69void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { 95void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
70 LOG_WARNING(Service_IRS, "(STUBBED) called"); 96 IPC::RequestParser rp{ctx};
97 struct Parameters {
98 IrCameraHandle camera_handle;
99 INSERT_PADDING_WORDS_NOINIT(1);
100 u64 applet_resource_user_id;
101 PackedMomentProcessorConfig processor_config;
102 };
103 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
104
105 const auto parameters{rp.PopRaw<Parameters>()};
106
107 LOG_WARNING(Service_IRS,
108 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
109 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
110 parameters.applet_resource_user_id);
71 111
72 IPC::ResponseBuilder rb{ctx, 2}; 112 IPC::ResponseBuilder rb{ctx, 2};
73 rb.Push(ResultSuccess); 113 rb.Push(ResultSuccess);
74} 114}
75 115
76void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { 116void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
77 LOG_WARNING(Service_IRS, "(STUBBED) called"); 117 IPC::RequestParser rp{ctx};
118 struct Parameters {
119 IrCameraHandle camera_handle;
120 INSERT_PADDING_WORDS_NOINIT(1);
121 u64 applet_resource_user_id;
122 PackedClusteringProcessorConfig processor_config;
123 };
124 static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
125
126 const auto parameters{rp.PopRaw<Parameters>()};
127
128 LOG_WARNING(Service_IRS,
129 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
130 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
131 parameters.applet_resource_user_id);
78 132
79 IPC::ResponseBuilder rb{ctx, 2}; 133 IPC::ResponseBuilder rb{ctx, 2};
80 rb.Push(ResultSuccess); 134 rb.Push(ResultSuccess);
81} 135}
82 136
83void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { 137void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
84 LOG_WARNING(Service_IRS, "(STUBBED) called"); 138 IPC::RequestParser rp{ctx};
139 struct Parameters {
140 IrCameraHandle camera_handle;
141 INSERT_PADDING_WORDS_NOINIT(1);
142 u64 applet_resource_user_id;
143 PackedImageTransferProcessorConfig processor_config;
144 u32 transfer_memory_size;
145 };
146 static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
147
148 const auto parameters{rp.PopRaw<Parameters>()};
149 const auto t_mem_handle{ctx.GetCopyHandle(0)};
150
151 auto t_mem =
152 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
153
154 LOG_WARNING(Service_IRS,
155 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
156 "applet_resource_user_id={}",
157 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
158 parameters.transfer_memory_size, parameters.applet_resource_user_id);
85 159
86 IPC::ResponseBuilder rb{ctx, 2}; 160 IPC::ResponseBuilder rb{ctx, 2};
87 rb.Push(ResultSuccess); 161 rb.Push(ResultSuccess);
88} 162}
89 163
90void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { 164void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
91 LOG_WARNING(Service_IRS, "(STUBBED) called"); 165 IPC::RequestParser rp{ctx};
166 struct Parameters {
167 IrCameraHandle camera_handle;
168 INSERT_PADDING_WORDS_NOINIT(1);
169 u64 applet_resource_user_id;
170 };
171 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
172
173 const auto parameters{rp.PopRaw<Parameters>()};
174
175 LOG_WARNING(Service_IRS,
176 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
177 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
178 parameters.applet_resource_user_id);
92 179
93 IPC::ResponseBuilder rb{ctx, 5}; 180 IPC::ResponseBuilder rb{ctx, 5};
94 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
@@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
97} 184}
98 185
99void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { 186void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
100 LOG_WARNING(Service_IRS, "(STUBBED) called"); 187 IPC::RequestParser rp{ctx};
188 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
189 const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
190 const auto applet_resource_user_id{rp.Pop<u64>()};
191
192 LOG_WARNING(Service_IRS,
193 "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
194 "applet_resource_user_id={}",
195 camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
196 processor_config.required_mcu_version.major,
197 processor_config.required_mcu_version.minor, applet_resource_user_id);
101 198
102 IPC::ResponseBuilder rb{ctx, 2}; 199 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultSuccess); 200 rb.Push(ResultSuccess);
104} 201}
105 202
106void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { 203void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
107 LOG_WARNING(Service_IRS, "(STUBBED) called"); 204 IPC::RequestParser rp{ctx};
205 const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
206
207 if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
208 npad_id != Core::HID::NpadIdType::Handheld) {
209 IPC::ResponseBuilder rb{ctx, 2};
210 rb.Push(InvalidNpadId);
211 return;
212 }
213
214 IrCameraHandle camera_handle{
215 .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
216 .npad_type = Core::HID::NpadStyleIndex::None,
217 };
218
219 LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
220 npad_id, camera_handle.npad_id, camera_handle.npad_type);
108 221
109 IPC::ResponseBuilder rb{ctx, 3}; 222 IPC::ResponseBuilder rb{ctx, 3};
110 rb.Push(ResultSuccess); 223 rb.Push(ResultSuccess);
111 rb.PushRaw<u32>(device_handle); 224 rb.PushRaw(camera_handle);
112} 225}
113 226
114void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { 227void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
115 LOG_WARNING(Service_IRS, "(STUBBED) called"); 228 IPC::RequestParser rp{ctx};
229 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
230 const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
231 const auto applet_resource_user_id{rp.Pop<u64>()};
232
233 LOG_WARNING(
234 Service_IRS,
235 "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
236 camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
237 processor_config.required_mcu_version.minor, applet_resource_user_id);
116 238
117 IPC::ResponseBuilder rb{ctx, 2}; 239 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(ResultSuccess); 240 rb.Push(ResultSuccess);
119} 241}
120 242
121void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { 243void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
122 LOG_WARNING(Service_IRS, "(STUBBED) called"); 244 IPC::RequestParser rp{ctx};
245 struct Parameters {
246 IrCameraHandle camera_handle;
247 INSERT_PADDING_WORDS_NOINIT(1);
248 u64 applet_resource_user_id;
249 };
250 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
251
252 const auto parameters{rp.PopRaw<Parameters>()};
253
254 LOG_WARNING(Service_IRS,
255 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
256 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
257 parameters.applet_resource_user_id);
123 258
124 IPC::ResponseBuilder rb{ctx, 2}; 259 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(ResultSuccess); 260 rb.Push(ResultSuccess);
126} 261}
127 262
128void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { 263void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
129 LOG_WARNING(Service_IRS, "(STUBBED) called"); 264 IPC::RequestParser rp{ctx};
265 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
266 const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
267 const auto applet_resource_user_id{rp.Pop<u64>()};
268
269 LOG_WARNING(
270 Service_IRS,
271 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
272 camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
273 mcu_version.minor);
130 274
131 IPC::ResponseBuilder rb{ctx, 2}; 275 IPC::ResponseBuilder rb{ctx, 2};
132 rb.Push(ResultSuccess); 276 rb.Push(ResultSuccess);
133} 277}
134 278
135void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { 279void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
136 LOG_WARNING(Service_IRS, "(STUBBED) called"); 280 IPC::RequestParser rp{ctx};
281 struct Parameters {
282 IrCameraHandle camera_handle;
283 PackedFunctionLevel function_level;
284 u64 applet_resource_user_id;
285 };
286 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
287
288 const auto parameters{rp.PopRaw<Parameters>()};
289
290 LOG_WARNING(Service_IRS,
291 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
292 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
293 parameters.applet_resource_user_id);
137 294
138 IPC::ResponseBuilder rb{ctx, 2}; 295 IPC::ResponseBuilder rb{ctx, 2};
139 rb.Push(ResultSuccess); 296 rb.Push(ResultSuccess);
140} 297}
141 298
142void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { 299void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
143 LOG_WARNING(Service_IRS, "(STUBBED) called"); 300 IPC::RequestParser rp{ctx};
301 struct Parameters {
302 IrCameraHandle camera_handle;
303 INSERT_PADDING_WORDS_NOINIT(1);
304 u64 applet_resource_user_id;
305 PackedImageTransferProcessorExConfig processor_config;
306 u64 transfer_memory_size;
307 };
308 static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
309
310 const auto parameters{rp.PopRaw<Parameters>()};
311 const auto t_mem_handle{ctx.GetCopyHandle(0)};
312
313 auto t_mem =
314 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
315
316 LOG_WARNING(Service_IRS,
317 "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
318 "applet_resource_user_id={}",
319 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
320 parameters.transfer_memory_size, parameters.applet_resource_user_id);
144 321
145 IPC::ResponseBuilder rb{ctx, 2}; 322 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultSuccess); 323 rb.Push(ResultSuccess);
147} 324}
148 325
149void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { 326void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
150 LOG_WARNING(Service_IRS, "(STUBBED) called"); 327 IPC::RequestParser rp{ctx};
328 const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
329 const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
330 const auto applet_resource_user_id{rp.Pop<u64>()};
331
332 LOG_WARNING(Service_IRS,
333 "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
334 "applet_resource_user_id={}",
335 camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
336 processor_config.required_mcu_version.major,
337 processor_config.required_mcu_version.minor, applet_resource_user_id);
151 338
152 IPC::ResponseBuilder rb{ctx, 2}; 339 IPC::ResponseBuilder rb{ctx, 2};
153 rb.Push(ResultSuccess); 340 rb.Push(ResultSuccess);
154} 341}
155 342
156void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { 343void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
157 LOG_WARNING(Service_IRS, "(STUBBED) called"); 344 IPC::RequestParser rp{ctx};
345 struct Parameters {
346 IrCameraHandle camera_handle;
347 INSERT_PADDING_WORDS_NOINIT(1);
348 u64 applet_resource_user_id;
349 };
350 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
351
352 const auto parameters{rp.PopRaw<Parameters>()};
353
354 LOG_WARNING(Service_IRS,
355 "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
356 parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
357 parameters.applet_resource_user_id);
158 358
159 IPC::ResponseBuilder rb{ctx, 2}; 359 IPC::ResponseBuilder rb{ctx, 2};
160 rb.Push(ResultSuccess); 360 rb.Push(ResultSuccess);
161} 361}
162 362
163void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { 363void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
164 LOG_WARNING(Service_IRS, "(STUBBED) called"); 364 IPC::RequestParser rp{ctx};
365 struct Parameters {
366 PackedFunctionLevel function_level;
367 INSERT_PADDING_WORDS_NOINIT(1);
368 u64 applet_resource_user_id;
369 };
370 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
371
372 const auto parameters{rp.PopRaw<Parameters>()};
373
374 LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
375 parameters.function_level.function_level, parameters.applet_resource_user_id);
165 376
166 IPC::ResponseBuilder rb{ctx, 2}; 377 IPC::ResponseBuilder rb{ctx, 2};
167 rb.Push(ResultSuccess); 378 rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index efb29d3fd..361dc2213 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/hid/hid_types.h"
6#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
7 8
8namespace Core { 9namespace Core {
@@ -17,6 +18,235 @@ public:
17 ~IRS() override; 18 ~IRS() override;
18 19
19private: 20private:
21 // This is nn::irsensor::IrCameraStatus
22 enum IrCameraStatus : u32 {
23 Available,
24 Unsupported,
25 Unconnected,
26 };
27
28 // This is nn::irsensor::IrCameraInternalStatus
29 enum IrCameraInternalStatus : u32 {
30 Stopped,
31 FirmwareUpdateNeeded,
32 Unkown2,
33 Unkown3,
34 Unkown4,
35 FirmwareVersionRequested,
36 FirmwareVersionIsInvalid,
37 Ready,
38 Setting,
39 };
40
41 // This is nn::irsensor::detail::StatusManager::IrSensorMode
42 enum IrSensorMode : u64 {
43 None,
44 MomentProcessor,
45 ClusteringProcessor,
46 ImageTransferProcessor,
47 PointingProcessorMarker,
48 TeraPluginProcessor,
49 IrLedProcessor,
50 };
51
52 // This is nn::irsensor::ImageProcessorStatus
53 enum ImageProcessorStatus : u8 {
54 stopped,
55 running,
56 };
57
58 // This is nn::irsensor::ImageTransferProcessorFormat
59 enum ImageTransferProcessorFormat : u8 {
60 Size320x240,
61 Size160x120,
62 Size80x60,
63 Size40x30,
64 Size20x15,
65 };
66
67 // This is nn::irsensor::AdaptiveClusteringMode
68 enum AdaptiveClusteringMode : u8 {
69 StaticFov,
70 DynamicFov,
71 };
72
73 // This is nn::irsensor::AdaptiveClusteringTargetDistance
74 enum AdaptiveClusteringTargetDistance : u8 {
75 Near,
76 Middle,
77 Far,
78 };
79
80 // This is nn::irsensor::IrsHandAnalysisMode
81 enum IrsHandAnalysisMode : u8 {
82 Silhouette,
83 Image,
84 SilhoueteAndImage,
85 SilhuetteOnly,
86 };
87
88 // This is nn::irsensor::IrSensorFunctionLevel
89 enum IrSensorFunctionLevel : u8 {
90 unknown0,
91 unknown1,
92 unknown2,
93 unknown3,
94 unknown4,
95 };
96
97 // This is nn::irsensor::IrCameraHandle
98 struct IrCameraHandle {
99 u8 npad_id{};
100 Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
101 INSERT_PADDING_BYTES(2);
102 };
103 static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
104
105 struct IrsRect {
106 s16 x;
107 s16 y;
108 s16 width;
109 s16 height;
110 };
111
112 // This is nn::irsensor::PackedMcuVersion
113 struct PackedMcuVersion {
114 u16 major;
115 u16 minor;
116 };
117 static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
118
119 // This is nn::irsensor::MomentProcessorConfig
120 struct MomentProcessorConfig {
121 u64 exposire_time;
122 u8 light_target;
123 u8 gain;
124 u8 is_negative_used;
125 INSERT_PADDING_BYTES(7);
126 IrsRect window_of_interest;
127 u8 preprocess;
128 u8 preprocess_intensity_threshold;
129 INSERT_PADDING_BYTES(5);
130 };
131 static_assert(sizeof(MomentProcessorConfig) == 0x28,
132 "MomentProcessorConfig is an invalid size");
133
134 // This is nn::irsensor::PackedMomentProcessorConfig
135 struct PackedMomentProcessorConfig {
136 u64 exposire_time;
137 u8 light_target;
138 u8 gain;
139 u8 is_negative_used;
140 INSERT_PADDING_BYTES(5);
141 IrsRect window_of_interest;
142 PackedMcuVersion required_mcu_version;
143 u8 preprocess;
144 u8 preprocess_intensity_threshold;
145 INSERT_PADDING_BYTES(2);
146 };
147 static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
148 "PackedMomentProcessorConfig is an invalid size");
149
150 // This is nn::irsensor::ClusteringProcessorConfig
151 struct ClusteringProcessorConfig {
152 u64 exposire_time;
153 u32 light_target;
154 u32 gain;
155 u8 is_negative_used;
156 INSERT_PADDING_BYTES(7);
157 IrsRect window_of_interest;
158 u32 pixel_count_min;
159 u32 pixel_count_max;
160 u32 object_intensity_min;
161 u8 is_external_light_filter_enabled;
162 INSERT_PADDING_BYTES(3);
163 };
164 static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
165 "ClusteringProcessorConfig is an invalid size");
166
167 // This is nn::irsensor::PackedClusteringProcessorConfig
168 struct PackedClusteringProcessorConfig {
169 u64 exposire_time;
170 u8 light_target;
171 u8 gain;
172 u8 is_negative_used;
173 INSERT_PADDING_BYTES(5);
174 IrsRect window_of_interest;
175 PackedMcuVersion required_mcu_version;
176 u32 pixel_count_min;
177 u32 pixel_count_max;
178 u32 object_intensity_min;
179 u8 is_external_light_filter_enabled;
180 INSERT_PADDING_BYTES(2);
181 };
182 static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
183 "PackedClusteringProcessorConfig is an invalid size");
184
185 // This is nn::irsensor::PackedImageTransferProcessorConfig
186 struct PackedImageTransferProcessorConfig {
187 u64 exposire_time;
188 u8 light_target;
189 u8 gain;
190 u8 is_negative_used;
191 INSERT_PADDING_BYTES(5);
192 PackedMcuVersion required_mcu_version;
193 u8 format;
194 INSERT_PADDING_BYTES(3);
195 };
196 static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
197 "PackedImageTransferProcessorConfig is an invalid size");
198
199 // This is nn::irsensor::PackedTeraPluginProcessorConfig
200 struct PackedTeraPluginProcessorConfig {
201 PackedMcuVersion required_mcu_version;
202 u8 mode;
203 INSERT_PADDING_BYTES(3);
204 };
205 static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
206 "PackedTeraPluginProcessorConfig is an invalid size");
207
208 // This is nn::irsensor::PackedPointingProcessorConfig
209 struct PackedPointingProcessorConfig {
210 IrsRect window_of_interest;
211 PackedMcuVersion required_mcu_version;
212 };
213 static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
214 "PackedPointingProcessorConfig is an invalid size");
215
216 // This is nn::irsensor::PackedFunctionLevel
217 struct PackedFunctionLevel {
218 IrSensorFunctionLevel function_level;
219 INSERT_PADDING_BYTES(3);
220 };
221 static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
222
223 // This is nn::irsensor::PackedImageTransferProcessorExConfig
224 struct PackedImageTransferProcessorExConfig {
225 u64 exposire_time;
226 u8 light_target;
227 u8 gain;
228 u8 is_negative_used;
229 INSERT_PADDING_BYTES(5);
230 PackedMcuVersion required_mcu_version;
231 ImageTransferProcessorFormat origin_format;
232 ImageTransferProcessorFormat trimming_format;
233 u16 trimming_start_x;
234 u16 trimming_start_y;
235 u8 is_external_light_filter_enabled;
236 INSERT_PADDING_BYTES(3);
237 };
238 static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
239 "PackedImageTransferProcessorExConfig is an invalid size");
240
241 // This is nn::irsensor::PackedIrLedProcessorConfig
242 struct PackedIrLedProcessorConfig {
243 PackedMcuVersion required_mcu_version;
244 u8 light_target;
245 INSERT_PADDING_BYTES(3);
246 };
247 static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
248 "PackedIrLedProcessorConfig is an invalid size");
249
20 void ActivateIrsensor(Kernel::HLERequestContext& ctx); 250 void ActivateIrsensor(Kernel::HLERequestContext& ctx);
21 void DeactivateIrsensor(Kernel::HLERequestContext& ctx); 251 void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
22 void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); 252 void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -35,8 +265,6 @@ private:
35 void RunIrLedProcessor(Kernel::HLERequestContext& ctx); 265 void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
36 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); 266 void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
37 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); 267 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
38
39 const u32 device_handle{0xABCD};
40}; 268};
41 269
42class IRS_SYS final : public ServiceFramework<IRS_SYS> { 270class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
index 19bd85b6c..4ed3f02e2 100644
--- a/src/core/hle/service/jit/jit_context.cpp
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -11,10 +11,13 @@
11#include "common/alignment.h" 11#include "common/alignment.h"
12#include "common/common_funcs.h" 12#include "common/common_funcs.h"
13#include "common/div_ceil.h" 13#include "common/div_ceil.h"
14#include "common/elf.h"
14#include "common/logging/log.h" 15#include "common/logging/log.h"
15#include "core/hle/service/jit/jit_context.h" 16#include "core/hle/service/jit/jit_context.h"
16#include "core/memory.h" 17#include "core/memory.h"
17 18
19using namespace Common::ELF;
20
18namespace Service::JIT { 21namespace Service::JIT {
19 22
20constexpr std::array<u8, 8> SVC0_ARM64 = { 23constexpr std::array<u8, 8> SVC0_ARM64 = {
@@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{
26 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", 29 "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
27}; 30};
28 31
29struct Elf64_Dyn {
30 u64 d_tag;
31 u64 d_un;
32};
33
34struct Elf64_Rela {
35 u64 r_offset;
36 u64 r_info;
37 s64 r_addend;
38};
39
40static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
41 return static_cast<u32>(rela->r_info);
42}
43
44constexpr int DT_RELA = 7; /* Address of Rela relocs */
45constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
46constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
47
48constexpr size_t STACK_ALIGN = 16; 32constexpr size_t STACK_ALIGN = 16;
49 33
50class JITContextImpl; 34class JITContextImpl;
@@ -206,17 +190,17 @@ public:
206 if (!dyn.d_tag) { 190 if (!dyn.d_tag) {
207 break; 191 break;
208 } 192 }
209 if (dyn.d_tag == DT_RELA) { 193 if (dyn.d_tag == ElfDtRela) {
210 rela_dyn = dyn.d_un; 194 rela_dyn = dyn.d_un.d_ptr;
211 } 195 }
212 if (dyn.d_tag == DT_RELASZ) { 196 if (dyn.d_tag == ElfDtRelasz) {
213 num_rela = dyn.d_un / sizeof(Elf64_Rela); 197 num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
214 } 198 }
215 } 199 }
216 200
217 for (size_t i = 0; i < num_rela; i++) { 201 for (size_t i = 0; i < num_rela; i++) {
218 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; 202 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
219 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) { 203 if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
220 continue; 204 continue;
221 } 205 }
222 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; 206 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
index d7db77aff..4b3d5efd6 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
89 89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); 90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91 91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to 92 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side. 93 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) { 94 if (out_buffer->acquire_called) {
@@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
139 ++current; 131 ++current;
140 } 132 }
141 133
142 if (slots[slot].buffer_state == BufferState::Acquired) { 134 slots[slot].buffer_state = BufferState::Free;
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152 135
153 slots[slot].needs_cleanup_on_release = false; 136 listener = core->connected_producer_listener;
154 137
155 return Status::StaleBufferSlot; 138 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162 139
163 core->SignalDequeueCondition(); 140 core->SignalDequeueCondition();
164 } 141 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index d4e8b44d0..ea4a14ea4 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) {
84 84
85 slots[slot].graphic_buffer.reset(); 85 slots[slot].graphic_buffer.reset();
86 86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free; 87 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX; 88 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false; 89 slots[slot].acquire_called = false;
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
index 6b3e87446..0cd0e9964 100644
--- a/src/core/hle/service/nvflinger/buffer_slot.h
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -31,7 +31,6 @@ struct BufferSlot final {
31 u64 frame_number{}; 31 u64 frame_number{};
32 Fence fence; 32 Fence fence;
33 bool acquire_called{}; 33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{}; 34 bool attached_by_consumer{};
36 bool is_preallocated{}; 35 bool is_preallocated{};
37}; 36};
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 449a5ac96..eeec34436 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -110,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) {
110 } 110 }
111} 111}
112 112
113static constexpr int GetMonthLength(bool is_leap_year, int month) { 113static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
114 constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 114 constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
115 constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 115 constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
116 31, 31, 30, 31, 30, 31};
117 return is_leap_year ? month_lengths_leap[month] : month_lengths[month]; 116 return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
118} 117}
119 118
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index cf5933699..dfb10c34f 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -6,6 +6,7 @@
6#include <memory> 6#include <memory>
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/elf.h"
9#include "common/logging/log.h" 10#include "common/logging/log.h"
10#include "core/hle/kernel/code_set.h" 11#include "core/hle/kernel/code_set.h"
11#include "core/hle/kernel/k_page_table.h" 12#include "core/hle/kernel/k_page_table.h"
@@ -13,159 +14,7 @@
13#include "core/loader/elf.h" 14#include "core/loader/elf.h"
14#include "core/memory.h" 15#include "core/memory.h"
15 16
16//////////////////////////////////////////////////////////////////////////////////////////////////// 17using namespace Common::ELF;
17// ELF Header Constants
18
19// File type
20enum ElfType {
21 ET_NONE = 0,
22 ET_REL = 1,
23 ET_EXEC = 2,
24 ET_DYN = 3,
25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF,
28};
29
30// Machine/Architecture
31enum ElfMachine {
32 EM_NONE = 0,
33 EM_M32 = 1,
34 EM_SPARC = 2,
35 EM_386 = 3,
36 EM_68K = 4,
37 EM_88K = 5,
38 EM_860 = 7,
39 EM_MIPS = 8
40};
41
42// File version
43#define EV_NONE 0
44#define EV_CURRENT 1
45
46// Identification index
47#define EI_MAG0 0
48#define EI_MAG1 1
49#define EI_MAG2 2
50#define EI_MAG3 3
51#define EI_CLASS 4
52#define EI_DATA 5
53#define EI_VERSION 6
54#define EI_PAD 7
55#define EI_NIDENT 16
56
57// Sections constants
58
59// Section types
60#define SHT_NULL 0
61#define SHT_PROGBITS 1
62#define SHT_SYMTAB 2
63#define SHT_STRTAB 3
64#define SHT_RELA 4
65#define SHT_HASH 5
66#define SHT_DYNAMIC 6
67#define SHT_NOTE 7
68#define SHT_NOBITS 8
69#define SHT_REL 9
70#define SHT_SHLIB 10
71#define SHT_DYNSYM 11
72#define SHT_LOPROC 0x70000000
73#define SHT_HIPROC 0x7FFFFFFF
74#define SHT_LOUSER 0x80000000
75#define SHT_HIUSER 0xFFFFFFFF
76
77// Section flags
78enum ElfSectionFlags {
79 SHF_WRITE = 0x1,
80 SHF_ALLOC = 0x2,
81 SHF_EXECINSTR = 0x4,
82 SHF_MASKPROC = 0xF0000000,
83};
84
85// Segment types
86#define PT_NULL 0
87#define PT_LOAD 1
88#define PT_DYNAMIC 2
89#define PT_INTERP 3
90#define PT_NOTE 4
91#define PT_SHLIB 5
92#define PT_PHDR 6
93#define PT_LOPROC 0x70000000
94#define PT_HIPROC 0x7FFFFFFF
95
96// Segment flags
97#define PF_X 0x1
98#define PF_W 0x2
99#define PF_R 0x4
100#define PF_MASKPROC 0xF0000000
101
102typedef unsigned int Elf32_Addr;
103typedef unsigned short Elf32_Half;
104typedef unsigned int Elf32_Off;
105typedef signed int Elf32_Sword;
106typedef unsigned int Elf32_Word;
107
108////////////////////////////////////////////////////////////////////////////////////////////////////
109// ELF file header
110
111struct Elf32_Ehdr {
112 unsigned char e_ident[EI_NIDENT];
113 Elf32_Half e_type;
114 Elf32_Half e_machine;
115 Elf32_Word e_version;
116 Elf32_Addr e_entry;
117 Elf32_Off e_phoff;
118 Elf32_Off e_shoff;
119 Elf32_Word e_flags;
120 Elf32_Half e_ehsize;
121 Elf32_Half e_phentsize;
122 Elf32_Half e_phnum;
123 Elf32_Half e_shentsize;
124 Elf32_Half e_shnum;
125 Elf32_Half e_shstrndx;
126};
127
128// Section header
129struct Elf32_Shdr {
130 Elf32_Word sh_name;
131 Elf32_Word sh_type;
132 Elf32_Word sh_flags;
133 Elf32_Addr sh_addr;
134 Elf32_Off sh_offset;
135 Elf32_Word sh_size;
136 Elf32_Word sh_link;
137 Elf32_Word sh_info;
138 Elf32_Word sh_addralign;
139 Elf32_Word sh_entsize;
140};
141
142// Segment header
143struct Elf32_Phdr {
144 Elf32_Word p_type;
145 Elf32_Off p_offset;
146 Elf32_Addr p_vaddr;
147 Elf32_Addr p_paddr;
148 Elf32_Word p_filesz;
149 Elf32_Word p_memsz;
150 Elf32_Word p_flags;
151 Elf32_Word p_align;
152};
153
154// Symbol table entry
155struct Elf32_Sym {
156 Elf32_Word st_name;
157 Elf32_Addr st_value;
158 Elf32_Word st_size;
159 unsigned char st_info;
160 unsigned char st_other;
161 Elf32_Half st_shndx;
162};
163
164// Relocation entries
165struct Elf32_Rel {
166 Elf32_Addr r_offset;
167 Elf32_Word r_info;
168};
169 18
170//////////////////////////////////////////////////////////////////////////////////////////////////// 19////////////////////////////////////////////////////////////////////////////////////////////////////
171// ElfReader class 20// ElfReader class
@@ -193,11 +42,11 @@ public:
193 } 42 }
194 43
195 // Quick accessors 44 // Quick accessors
196 ElfType GetType() const { 45 u16 GetType() const {
197 return (ElfType)(header->e_type); 46 return header->e_type;
198 } 47 }
199 ElfMachine GetMachine() const { 48 u16 GetMachine() const {
200 return (ElfMachine)(header->e_machine); 49 return header->e_machine;
201 } 50 }
202 VAddr GetEntryPoint() const { 51 VAddr GetEntryPoint() const {
203 return entryPoint; 52 return entryPoint;
@@ -220,13 +69,13 @@ public:
220 const u8* GetSectionDataPtr(int section) const { 69 const u8* GetSectionDataPtr(int section) const {
221 if (section < 0 || section >= header->e_shnum) 70 if (section < 0 || section >= header->e_shnum)
222 return nullptr; 71 return nullptr;
223 if (sections[section].sh_type != SHT_NOBITS) 72 if (sections[section].sh_type != ElfShtNobits)
224 return GetPtr(sections[section].sh_offset); 73 return GetPtr(sections[section].sh_offset);
225 else 74 else
226 return nullptr; 75 return nullptr;
227 } 76 }
228 bool IsCodeSection(int section) const { 77 bool IsCodeSection(int section) const {
229 return sections[section].sh_type == SHT_PROGBITS; 78 return sections[section].sh_type == ElfShtProgBits;
230 } 79 }
231 const u8* GetSegmentPtr(int segment) { 80 const u8* GetSegmentPtr(int segment) {
232 return GetPtr(segments[segment].p_offset); 81 return GetPtr(segments[segment].p_offset);
@@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) {
256} 105}
257 106
258const char* ElfReader::GetSectionName(int section) const { 107const char* ElfReader::GetSectionName(int section) const {
259 if (sections[section].sh_type == SHT_NULL) 108 if (sections[section].sh_type == ElfShtNull)
260 return nullptr; 109 return nullptr;
261 110
262 int name_offset = sections[section].sh_name; 111 int name_offset = sections[section].sh_name;
@@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
272 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); 121 LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
273 122
274 // Should we relocate? 123 // Should we relocate?
275 relocate = (header->e_type != ET_EXEC); 124 relocate = (header->e_type != ElfTypeExec);
276 125
277 if (relocate) { 126 if (relocate) {
278 LOG_DEBUG(Loader, "Relocatable module"); 127 LOG_DEBUG(Loader, "Relocatable module");
@@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
288 u64 total_image_size = 0; 137 u64 total_image_size = 0;
289 for (unsigned int i = 0; i < header->e_phnum; ++i) { 138 for (unsigned int i = 0; i < header->e_phnum; ++i) {
290 const Elf32_Phdr* p = &segments[i]; 139 const Elf32_Phdr* p = &segments[i];
291 if (p->p_type == PT_LOAD) { 140 if (p->p_type == ElfPtLoad) {
292 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; 141 total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
293 } 142 }
294 } 143 }
@@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
303 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, 152 LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
304 p->p_vaddr, p->p_filesz, p->p_memsz); 153 p->p_vaddr, p->p_filesz, p->p_memsz);
305 154
306 if (p->p_type == PT_LOAD) { 155 if (p->p_type == ElfPtLoad) {
307 Kernel::CodeSet::Segment* codeset_segment; 156 Kernel::CodeSet::Segment* codeset_segment;
308 u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); 157 u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
309 if (permission_flags == (PF_R | PF_X)) { 158 if (permission_flags == (ElfPfRead | ElfPfExec)) {
310 codeset_segment = &codeset.CodeSegment(); 159 codeset_segment = &codeset.CodeSegment();
311 } else if (permission_flags == (PF_R)) { 160 } else if (permission_flags == (ElfPfRead)) {
312 codeset_segment = &codeset.RODataSegment(); 161 codeset_segment = &codeset.RODataSegment();
313 } else if (permission_flags == (PF_R | PF_W)) { 162 } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
314 codeset_segment = &codeset.DataSegment(); 163 codeset_segment = &codeset.DataSegment();
315 } else { 164 } else {
316 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, 165 LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 28d30eee2..7534de01e 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -594,6 +594,19 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
594 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory; 594 return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
595} 595}
596 596
597bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
598 VAddr end = base + size;
599 VAddr page = Common::AlignDown(base, PAGE_SIZE);
600
601 for (; page < end; page += PAGE_SIZE) {
602 if (!IsValidVirtualAddress(page)) {
603 return false;
604 }
605 }
606
607 return true;
608}
609
597u8* Memory::GetPointer(VAddr vaddr) { 610u8* Memory::GetPointer(VAddr vaddr) {
598 return impl->GetPointer(vaddr); 611 return impl->GetPointer(vaddr);
599} 612}
diff --git a/src/core/memory.h b/src/core/memory.h
index b5721b740..58cc27b29 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -96,6 +96,17 @@ public:
96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const; 96 [[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
97 97
98 /** 98 /**
99 * Checks whether or not the supplied range of addresses are all valid
100 * virtual addresses for the current process.
101 *
102 * @param base The address to begin checking.
103 * @param size The amount of bytes to check.
104 *
105 * @returns True if all bytes in the given range are valid, false otherwise.
106 */
107 [[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
108
109 /**
99 * Gets a pointer to the given address. 110 * Gets a pointer to the given address.
100 * 111 *
101 * @param vaddr Virtual address to retrieve a pointer to. 112 * @param vaddr Virtual address to retrieve a pointer to.
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 6a6325e38..256695804 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -277,3 +277,7 @@ else()
277 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable> 277 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
278 ) 278 )
279endif() 279endif()
280
281if (ARCHITECTURE_x86_64)
282 target_link_libraries(video_core PRIVATE dynarmic)
283endif()
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index a95618913..c01431441 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -153,7 +153,7 @@ constexpr Vp9EntropyProbs default_probs{
153 .high_precision{128, 128}, 153 .high_precision{128, 128},
154}; 154};
155 155
156constexpr std::array<s32, 256> norm_lut{ 156constexpr std::array<u8, 256> norm_lut{
157 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 157 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
158 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 158 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
159 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 159 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@@ -164,7 +164,7 @@ constexpr std::array<s32, 256> norm_lut{
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
165}; 165};
166 166
167constexpr std::array<s32, 254> map_lut{ 167constexpr std::array<u8, 254> map_lut{
168 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 168 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
169 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54, 169 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
170 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 170 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
@@ -232,7 +232,7 @@ constexpr std::array<s32, 254> map_lut{
232 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1)); 232 std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
233 } 233 }
234 234
235 return map_lut[index]; 235 return static_cast<s32>(map_lut[index]);
236} 236}
237} // Anonymous namespace 237} // Anonymous namespace
238 238
@@ -819,7 +819,7 @@ void VpxRangeEncoder::Write(bool bit, s32 probability) {
819 local_range = range - split; 819 local_range = range - split;
820 } 820 }
821 821
822 s32 shift = norm_lut[local_range]; 822 s32 shift = static_cast<s32>(norm_lut[local_range]);
823 local_range <<= shift; 823 local_range <<= shift;
824 count += shift; 824 count += shift;
825 825
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 7d0cb8fce..3a4646289 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -595,8 +595,8 @@ void Maxwell3D::DrawArrays() {
595 595
596std::optional<u64> Maxwell3D::GetQueryResult() { 596std::optional<u64> Maxwell3D::GetQueryResult() {
597 switch (regs.query.query_get.select) { 597 switch (regs.query.query_get.select) {
598 case Regs::QuerySelect::Zero: 598 case Regs::QuerySelect::Payload:
599 return 0; 599 return regs.query.query_sequence;
600 case Regs::QuerySelect::SamplesPassed: 600 case Regs::QuerySelect::SamplesPassed:
601 // Deferred. 601 // Deferred.
602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, 602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c0c2c7d96..434ba0877 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -93,7 +93,7 @@ public:
93 }; 93 };
94 94
95 enum class QuerySelect : u32 { 95 enum class QuerySelect : u32 {
96 Zero = 0, 96 Payload = 0,
97 TimeElapsed = 2, 97 TimeElapsed = 2,
98 TransformFeedbackPrimitivesGenerated = 11, 98 TransformFeedbackPrimitivesGenerated = 11,
99 PrimitivesGenerated = 18, 99 PrimitivesGenerated = 18,
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index b79a73132..8479dc6d2 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -31,7 +31,8 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
31 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); 31 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
32 32
33 while (!stop_token.stop_requested()) { 33 while (!stop_token.stop_requested()) {
34 CommandDataContainer next = state.queue.PopWait(stop_token); 34 CommandDataContainer next;
35 state.queue.Pop(next, stop_token);
35 if (stop_token.stop_requested()) { 36 if (stop_token.stop_requested()) {
36 break; 37 break;
37 } 38 }
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 71cd35756..ad9fd5eff 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,7 +10,7 @@
10#include <thread> 10#include <thread>
11#include <variant> 11#include <variant>
12 12
13#include "common/threadsafe_queue.h" 13#include "common/bounded_threadsafe_queue.h"
14#include "video_core/framebuffer_config.h" 14#include "video_core/framebuffer_config.h"
15 15
16namespace Tegra { 16namespace Tegra {
@@ -96,9 +96,9 @@ struct CommandDataContainer {
96 96
97/// Struct used to synchronize the GPU thread 97/// Struct used to synchronize the GPU thread
98struct SynchState final { 98struct SynchState final {
99 using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>; 99 using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
100 std::mutex write_lock; 100 std::mutex write_lock;
101 CommandQueue queue; 101 CommandQueue queue{512}; // size must be 2^n
102 u64 last_fence{}; 102 u64 last_fence{};
103 std::atomic<u64> signaled_fence{}; 103 std::atomic<u64> signaled_fence{};
104 std::condition_variable_any cv; 104 std::condition_variable_any cv;
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 86fea61ae..75e055592 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -147,7 +147,7 @@ enum class SurfaceTarget {
147 TextureCubeArray, 147 TextureCubeArray,
148}; 148};
149 149
150constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{ 150constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
151 1, // A8B8G8R8_UNORM 151 1, // A8B8G8R8_UNORM
152 1, // A8B8G8R8_SNORM 152 1, // A8B8G8R8_SNORM
153 1, // A8B8G8R8_SINT 153 1, // A8B8G8R8_SINT
@@ -249,7 +249,7 @@ constexpr u32 DefaultBlockWidth(PixelFormat format) {
249 return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)]; 249 return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
250} 250}
251 251
252constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{ 252constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
253 1, // A8B8G8R8_UNORM 253 1, // A8B8G8R8_UNORM
254 1, // A8B8G8R8_SNORM 254 1, // A8B8G8R8_SNORM
255 1, // A8B8G8R8_SINT 255 1, // A8B8G8R8_SINT
@@ -351,7 +351,7 @@ constexpr u32 DefaultBlockHeight(PixelFormat format) {
351 return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)]; 351 return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
352} 352}
353 353
354constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{ 354constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
355 32, // A8B8G8R8_UNORM 355 32, // A8B8G8R8_UNORM
356 32, // A8B8G8R8_SNORM 356 32, // A8B8G8R8_SNORM
357 32, // A8B8G8R8_SINT 357 32, // A8B8G8R8_SINT
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp
index a5dd33fb2..4eb3913ee 100644
--- a/src/video_core/vulkan_common/vulkan_library.cpp
+++ b/src/video_core/vulkan_common/vulkan_library.cpp
@@ -5,11 +5,13 @@
5 5
6#include "common/dynamic_library.h" 6#include "common/dynamic_library.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_library.h" 9#include "video_core/vulkan_common/vulkan_library.h"
9 10
10namespace Vulkan { 11namespace Vulkan {
11 12
12Common::DynamicLibrary OpenLibrary() { 13Common::DynamicLibrary OpenLibrary() {
14 LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
13 Common::DynamicLibrary library; 15 Common::DynamicLibrary library;
14#ifdef __APPLE__ 16#ifdef __APPLE__
15 // Check if a path to a specific Vulkan library has been specified. 17 // Check if a path to a specific Vulkan library has been specified.
@@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() {
22 } 24 }
23#else 25#else
24 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); 26 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
27 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
25 if (!library.Open(filename.c_str())) { 28 if (!library.Open(filename.c_str())) {
26 // Android devices may not have libvulkan.so.1, only libvulkan.so. 29 // Android devices may not have libvulkan.so.1, only libvulkan.so.
27 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); 30 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
31 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
28 void(library.Open(filename.c_str())); 32 void(library.Open(filename.c_str()));
29 } 33 }
30#endif 34#endif
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 404acdd05..9259ca15e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,6 +30,8 @@ add_executable(yuzu
30 applets/qt_web_browser_scripts.h 30 applets/qt_web_browser_scripts.h
31 bootmanager.cpp 31 bootmanager.cpp
32 bootmanager.h 32 bootmanager.h
33 check_vulkan.cpp
34 check_vulkan.h
33 compatdb.ui 35 compatdb.ui
34 compatibility_list.cpp 36 compatibility_list.cpp
35 compatibility_list.h 37 compatibility_list.h
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
187 # Update source TS file if enabled 189 # Update source TS file if enabled
188 if (GENERATE_QT_TRANSLATION) 190 if (GENERATE_QT_TRANSLATION)
189 get_target_property(SRCS yuzu SOURCES) 191 get_target_property(SRCS yuzu SOURCES)
190 qt5_create_translation(QM_FILES 192 qt_create_translation(QM_FILES
191 ${SRCS} 193 ${SRCS}
192 ${UIS} 194 ${UIS}
193 ${YUZU_QT_LANGUAGES}/en.ts 195 ${YUZU_QT_LANGUAGES}/en.ts
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
203 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) 205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
204 206
205 # Compile TS files to QM files 207 # Compile TS files to QM files
206 qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 208 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
207 209
208 # Build a QRC file from the QM file list 210 # Build a QRC file from the QM file list
209 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
215 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") 217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
216 218
217 # Add the QRC file to package in all QM files 219 # Add the QRC file to package in all QM files
218 qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) 220 qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
219else() 221else()
220 set(LANGUAGES) 222 set(LANGUAGES)
221endif() 223endif()
@@ -236,7 +238,11 @@ if (APPLE)
236 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 238 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
237elseif(WIN32) 239elseif(WIN32)
238 # compile as a win32 gui application instead of a console application 240 # compile as a win32 gui application instead of a console application
239 target_link_libraries(yuzu PRIVATE Qt5::WinMain) 241 if (QT_VERSION VERSION_GREATER 6)
242 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
243 else()
244 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
245 endif()
240 if(MSVC) 246 if(MSVC)
241 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") 247 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
242 elseif(MINGW) 248 elseif(MINGW)
@@ -247,7 +253,7 @@ endif()
247create_target_directory_groups(yuzu) 253create_target_directory_groups(yuzu)
248 254
249target_link_libraries(yuzu PRIVATE common core input_common video_core) 255target_link_libraries(yuzu PRIVATE common core input_common video_core)
250target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) 256target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
251target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 257target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
252 258
253target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 259target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -255,7 +261,7 @@ if (NOT WIN32)
255 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 261 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
256endif() 262endif()
257if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 263if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
258 target_link_libraries(yuzu PRIVATE Qt5::DBus) 264 target_link_libraries(yuzu PRIVATE Qt::DBus)
259endif() 265endif()
260 266
261target_compile_definitions(yuzu PRIVATE 267target_compile_definitions(yuzu PRIVATE
@@ -291,7 +297,7 @@ if (USE_DISCORD_PRESENCE)
291endif() 297endif()
292 298
293if (YUZU_USE_QT_WEB_ENGINE) 299if (YUZU_USE_QT_WEB_ENGINE)
294 target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) 300 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
295 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 301 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
296endif () 302endif ()
297 303
@@ -319,3 +325,7 @@ endif()
319if (NOT APPLE) 325if (NOT APPLE)
320 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 326 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
321endif() 327endif()
328
329if (ARCHITECTURE_x86_64)
330 target_link_libraries(yuzu PRIVATE dynarmic)
331endif()
diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp
index cbcef7b45..eeff54359 100644
--- a/src/yuzu/about_dialog.cpp
+++ b/src/yuzu/about_dialog.cpp
@@ -19,7 +19,11 @@ AboutDialog::AboutDialog(QWidget* parent)
19 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build; 19 const auto yuzu_build_version = override_build.empty() ? yuzu_build : override_build;
20 20
21 ui->setupUi(this); 21 ui->setupUi(this);
22 ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200)); 22 // Try and request the icon from Qt theme (Linux?)
23 const QIcon yuzu_logo = QIcon::fromTheme(QStringLiteral("org.yuzu_emu.yuzu"));
24 if (!yuzu_logo.isNull()) {
25 ui->labelLogo->setPixmap(yuzu_logo.pixmap(200));
26 }
23 ui->labelBuildInfo->setText( 27 ui->labelBuildInfo->setText(
24 ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version), 28 ui->labelBuildInfo->text().arg(QString::fromStdString(yuzu_build_version),
25 QString::fromUtf8(Common::g_build_date).left(10))); 29 QString::fromUtf8(Common::g_build_date).left(10)));
diff --git a/src/yuzu/aboutdialog.ui b/src/yuzu/aboutdialog.ui
index 2f7ddc7f3..1dd7b74bf 100644
--- a/src/yuzu/aboutdialog.ui
+++ b/src/yuzu/aboutdialog.ui
@@ -26,8 +26,20 @@
26 <verstretch>0</verstretch> 26 <verstretch>0</verstretch>
27 </sizepolicy> 27 </sizepolicy>
28 </property> 28 </property>
29 <property name="maximumSize">
30 <size>
31 <width>200</width>
32 <height>200</height>
33 </size>
34 </property>
29 <property name="text"> 35 <property name="text">
30 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> 36 <string/>
37 </property>
38 <property name="pixmap">
39 <pixmap resource="../../dist/qt_themes/default/default.qrc">:/icons/default/256x256/yuzu.png</pixmap>
40 </property>
41 <property name="scaledContents">
42 <bool>true</bool>
31 </property> 43 </property>
32 </widget> 44 </widget>
33 </item> 45 </item>
@@ -152,7 +164,7 @@ p, li { white-space: pre-wrap; }
152 </layout> 164 </layout>
153 </widget> 165 </widget>
154 <resources> 166 <resources>
155 <include location="../../dist/icons/icons.qrc"/> 167 <include location="../../dist/qt_themes_default/default/default.qrc"/>
156 </resources> 168 </resources>
157 <connections> 169 <connections>
158 <connection> 170 <connection>
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8f0a6bbb8..bde465485 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -50,6 +50,7 @@ void EmuThread::run() {
50 50
51 auto& gpu = system.GPU(); 51 auto& gpu = system.GPU();
52 auto stop_token = stop_source.get_token(); 52 auto stop_token = stop_source.get_token();
53 bool debugger_should_start = system.DebuggerEnabled();
53 54
54 system.RegisterHostThread(); 55 system.RegisterHostThread();
55 56
@@ -89,6 +90,12 @@ void EmuThread::run() {
89 this->SetRunning(false); 90 this->SetRunning(false);
90 emit ErrorThrown(result, system.GetStatusDetails()); 91 emit ErrorThrown(result, system.GetStatusDetails());
91 } 92 }
93
94 if (debugger_should_start) {
95 system.InitializeDebugger();
96 debugger_should_start = false;
97 }
98
92 running_wait.Wait(); 99 running_wait.Wait();
93 result = system.Pause(); 100 result = system.Pause();
94 if (result != Core::SystemResultStatus::Success) { 101 if (result != Core::SystemResultStatus::Success) {
@@ -102,11 +109,9 @@ void EmuThread::run() {
102 was_active = true; 109 was_active = true;
103 emit DebugModeEntered(); 110 emit DebugModeEntered();
104 } 111 }
105 } else if (exec_step) {
106 UNIMPLEMENTED();
107 } else { 112 } else {
108 std::unique_lock lock{running_mutex}; 113 std::unique_lock lock{running_mutex};
109 running_cv.wait(lock, stop_token, [this] { return IsRunning() || exec_step; }); 114 running_cv.wait(lock, stop_token, [this] { return IsRunning(); });
110 } 115 }
111 } 116 }
112 117
@@ -747,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
747 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); 752 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y);
748 753
749 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
750 QCursor::setPos(mapToGlobal({center_x, center_y})); 755 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
751 } 756 }
752 757
753 emit MouseActivity(); 758 emit MouseActivity();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 841816564..d01538039 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -10,6 +10,7 @@
10#include <mutex> 10#include <mutex>
11 11
12#include <QImage> 12#include <QImage>
13#include <QStringList>
13#include <QThread> 14#include <QThread>
14#include <QTouchEvent> 15#include <QTouchEvent>
15#include <QWidget> 16#include <QWidget>
@@ -20,7 +21,6 @@
20class GRenderWindow; 21class GRenderWindow;
21class GMainWindow; 22class GMainWindow;
22class QKeyEvent; 23class QKeyEvent;
23class QStringList;
24 24
25namespace Core { 25namespace Core {
26enum class SystemResultStatus : u32; 26enum class SystemResultStatus : u32;
@@ -55,15 +55,6 @@ public:
55 void run() override; 55 void run() override;
56 56
57 /** 57 /**
58 * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
59 * @note This function is thread-safe
60 */
61 void ExecStep() {
62 exec_step = true;
63 running_cv.notify_all();
64 }
65
66 /**
67 * Sets whether the emulation thread is running or not 58 * Sets whether the emulation thread is running or not
68 * @param running Boolean value, set the emulation thread to running if true 59 * @param running Boolean value, set the emulation thread to running if true
69 * @note This function is thread-safe 60 * @note This function is thread-safe
@@ -99,7 +90,6 @@ public:
99 } 90 }
100 91
101private: 92private:
102 bool exec_step = false;
103 bool running = false; 93 bool running = false;
104 std::stop_source stop_source; 94 std::stop_source stop_source;
105 std::mutex running_mutex; 95 std::mutex running_mutex;
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp
new file mode 100644
index 000000000..e6d66ab34
--- /dev/null
+++ b/src/yuzu/check_vulkan.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/vulkan_common/vulkan_wrapper.h"
5
6#include <filesystem>
7#include <fstream>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "video_core/vulkan_common/vulkan_instance.h"
12#include "video_core/vulkan_common/vulkan_library.h"
13#include "yuzu/check_vulkan.h"
14#include "yuzu/uisettings.h"
15
16constexpr char TEMP_FILE_NAME[] = "vulkan_check";
17
18bool CheckVulkan() {
19 if (UISettings::values.has_broken_vulkan) {
20 return true;
21 }
22
23 LOG_DEBUG(Frontend, "Checking presence of Vulkan");
24
25 const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
26 const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
27
28 if (std::filesystem::exists(temp_file_loc)) {
29 LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
30
31 UISettings::values.has_broken_vulkan = true;
32 std::filesystem::remove(temp_file_loc);
33 return false;
34 }
35
36 std::ofstream temp_file_handle(temp_file_loc);
37 temp_file_handle.close();
38
39 try {
40 Vulkan::vk::InstanceDispatch dld;
41 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
42 const Vulkan::vk::Instance instance =
43 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
44
45 } catch (const Vulkan::vk::Exception& exception) {
46 LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
47 // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
48 // application, not when we can handle it.
49 }
50
51 std::filesystem::remove(temp_file_loc);
52 return true;
53}
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h
new file mode 100644
index 000000000..e4ea93582
--- /dev/null
+++ b/src/yuzu/check_vulkan.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6bool CheckVulkan();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index ac26b885b..9df4752be 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
71// UISetting::values.shortcuts, which is alphabetically ordered. 71// UISetting::values.shortcuts, which is alphabetically ordered.
72// clang-format off 72// clang-format off
73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 73const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
74 {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}},
75 {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}},
76 {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}},
77 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}},
78 {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}},
79 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}},
80 {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}},
81 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}},
82 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}},
83 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}},
84 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}},
85 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}},
86 {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}},
87 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}},
88 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}},
89 {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}},
90 {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}},
91 {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}},
92 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}},
93 {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}},
94 {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}},
95 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 95 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}},
96}}; 96}};
97// clang-format on 97// clang-format on
98 98
@@ -525,6 +525,9 @@ void Config::ReadDebuggingValues() {
525 // Intentionally not using the QT default setting as this is intended to be changed in the ini 525 // Intentionally not using the QT default setting as this is intended to be changed in the ini
526 Settings::values.record_frame_times = 526 Settings::values.record_frame_times =
527 qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); 527 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
528
529 ReadBasicSetting(Settings::values.use_gdbstub);
530 ReadBasicSetting(Settings::values.gdbstub_port);
528 ReadBasicSetting(Settings::values.program_args); 531 ReadBasicSetting(Settings::values.program_args);
529 ReadBasicSetting(Settings::values.dump_exefs); 532 ReadBasicSetting(Settings::values.dump_exefs);
530 ReadBasicSetting(Settings::values.dump_nso); 533 ReadBasicSetting(Settings::values.dump_nso);
@@ -679,6 +682,12 @@ void Config::ReadRendererValues() {
679 ReadGlobalSetting(Settings::values.bg_green); 682 ReadGlobalSetting(Settings::values.bg_green);
680 ReadGlobalSetting(Settings::values.bg_blue); 683 ReadGlobalSetting(Settings::values.bg_blue);
681 684
685 if (!global && UISettings::values.has_broken_vulkan &&
686 Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
687 !Settings::values.renderer_backend.UsingGlobal()) {
688 Settings::values.renderer_backend.SetGlobal(true);
689 }
690
682 if (global) { 691 if (global) {
683 ReadBasicSetting(Settings::values.renderer_debug); 692 ReadBasicSetting(Settings::values.renderer_debug);
684 ReadBasicSetting(Settings::values.renderer_shader_feedback); 693 ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -798,6 +807,7 @@ void Config::ReadUIValues() {
798 ReadBasicSetting(UISettings::values.pause_when_in_background); 807 ReadBasicSetting(UISettings::values.pause_when_in_background);
799 ReadBasicSetting(UISettings::values.mute_when_in_background); 808 ReadBasicSetting(UISettings::values.mute_when_in_background);
800 ReadBasicSetting(UISettings::values.hide_mouse); 809 ReadBasicSetting(UISettings::values.hide_mouse);
810 ReadBasicSetting(UISettings::values.has_broken_vulkan);
801 ReadBasicSetting(UISettings::values.disable_web_applet); 811 ReadBasicSetting(UISettings::values.disable_web_applet);
802 812
803 qt_config->endGroup(); 813 qt_config->endGroup();
@@ -1095,6 +1105,8 @@ void Config::SaveDebuggingValues() {
1095 1105
1096 // Intentionally not using the QT default setting as this is intended to be changed in the ini 1106 // Intentionally not using the QT default setting as this is intended to be changed in the ini
1097 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); 1107 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
1108 WriteBasicSetting(Settings::values.use_gdbstub);
1109 WriteBasicSetting(Settings::values.gdbstub_port);
1098 WriteBasicSetting(Settings::values.program_args); 1110 WriteBasicSetting(Settings::values.program_args);
1099 WriteBasicSetting(Settings::values.dump_exefs); 1111 WriteBasicSetting(Settings::values.dump_exefs);
1100 WriteBasicSetting(Settings::values.dump_nso); 1112 WriteBasicSetting(Settings::values.dump_nso);
@@ -1343,6 +1355,7 @@ void Config::SaveUIValues() {
1343 WriteBasicSetting(UISettings::values.pause_when_in_background); 1355 WriteBasicSetting(UISettings::values.pause_when_in_background);
1344 WriteBasicSetting(UISettings::values.mute_when_in_background); 1356 WriteBasicSetting(UISettings::values.mute_when_in_background);
1345 WriteBasicSetting(UISettings::values.hide_mouse); 1357 WriteBasicSetting(UISettings::values.hide_mouse);
1358 WriteBasicSetting(UISettings::values.has_broken_vulkan);
1346 WriteBasicSetting(UISettings::values.disable_web_applet); 1359 WriteBasicSetting(UISettings::values.disable_web_applet);
1347 1360
1348 qt_config->endGroup(); 1361 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index d6e8b5ead..343d2aee1 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -24,13 +24,18 @@ ConfigureDebug::ConfigureDebug(const Core::System& system_, QWidget* parent)
24 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir)); 24 QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir));
25 QDesktopServices::openUrl(QUrl::fromLocalFile(path)); 25 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
26 }); 26 });
27
28 connect(ui->toggle_gdbstub, &QCheckBox::toggled,
29 [&]() { ui->gdbport_spinbox->setEnabled(ui->toggle_gdbstub->isChecked()); });
27} 30}
28 31
29ConfigureDebug::~ConfigureDebug() = default; 32ConfigureDebug::~ConfigureDebug() = default;
30 33
31void ConfigureDebug::SetConfiguration() { 34void ConfigureDebug::SetConfiguration() {
32 const bool runtime_lock = !system.IsPoweredOn(); 35 const bool runtime_lock = !system.IsPoweredOn();
33 36 ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue());
37 ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue());
38 ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue());
34 ui->toggle_console->setEnabled(runtime_lock); 39 ui->toggle_console->setEnabled(runtime_lock);
35 ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); 40 ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
36 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); 41 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
@@ -71,6 +76,8 @@ void ConfigureDebug::SetConfiguration() {
71} 76}
72 77
73void ConfigureDebug::ApplyConfiguration() { 78void ConfigureDebug::ApplyConfiguration() {
79 Settings::values.use_gdbstub = ui->toggle_gdbstub->isChecked();
80 Settings::values.gdbstub_port = ui->gdbport_spinbox->value();
74 UISettings::values.show_console = ui->toggle_console->isChecked(); 81 UISettings::values.show_console = ui->toggle_console->isChecked();
75 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 82 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
76 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 83 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 863a3fd57..1152fa6c6 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -3,6 +3,60 @@
3 <class>ConfigureDebug</class> 3 <class>ConfigureDebug</class>
4 <widget class="QWidget" name="ConfigureDebug"> 4 <widget class="QWidget" name="ConfigureDebug">
5 <layout class="QVBoxLayout" name="verticalLayout_1"> 5 <layout class="QVBoxLayout" name="verticalLayout_1">
6 <item>
7 <layout class="QVBoxLayout" name="verticalLayout_2">
8 <item>
9 <widget class="QGroupBox" name="groupBox">
10 <property name="title">
11 <string>Debugger</string>
12 </property>
13 <layout class="QVBoxLayout" name="verticalLayout_3">
14 <item>
15 <layout class="QHBoxLayout" name="horizontalLayout_11">
16 <item>
17 <widget class="QCheckBox" name="toggle_gdbstub">
18 <property name="text">
19 <string>Enable GDB Stub</string>
20 </property>
21 </widget>
22 </item>
23 <item>
24 <spacer name="horizontalSpacer">
25 <property name="orientation">
26 <enum>Qt::Horizontal</enum>
27 </property>
28 <property name="sizeHint" stdset="0">
29 <size>
30 <width>40</width>
31 <height>20</height>
32 </size>
33 </property>
34 </spacer>
35 </item>
36 <item>
37 <widget class="QLabel" name="label_11">
38 <property name="text">
39 <string>Port:</string>
40 </property>
41 </widget>
42 </item>
43 <item>
44 <widget class="QSpinBox" name="gdbport_spinbox">
45 <property name="minimum">
46 <number>1024</number>
47 </property>
48 <property name="maximum">
49 <number>65535</number>
50 </property>
51 </widget>
52 </item>
53 </layout>
54 </item>
55 </layout>
56 </widget>
57 </item>
58 </layout>
59 </item>
6 <item> 60 <item>
7 <widget class="QGroupBox" name="groupBox_2"> 61 <widget class="QGroupBox" name="groupBox_2">
8 <property name="title"> 62 <property name="title">
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2f1435b10..85f34dc35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -17,6 +17,7 @@
17#include "video_core/vulkan_common/vulkan_library.h" 17#include "video_core/vulkan_common/vulkan_library.h"
18#include "yuzu/configuration/configuration_shared.h" 18#include "yuzu/configuration/configuration_shared.h"
19#include "yuzu/configuration/configure_graphics.h" 19#include "yuzu/configuration/configure_graphics.h"
20#include "yuzu/uisettings.h"
20 21
21ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) 22ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
22 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { 23 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
@@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
57 UpdateBackgroundColorButton(new_bg_color); 58 UpdateBackgroundColorButton(new_bg_color);
58 }); 59 });
59 60
61 connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
62 UISettings::values.has_broken_vulkan = false;
63
64 if (RetrieveVulkanDevices()) {
65 ui->api->setEnabled(true);
66 ui->button_check_vulkan->hide();
67
68 for (const auto& device : vulkan_devices) {
69 ui->device->addItem(device);
70 }
71 } else {
72 UISettings::values.has_broken_vulkan = true;
73 }
74 });
75
76 ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
77 ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
78
60 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 79 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
61 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 80 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
62} 81}
@@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
296 vulkan_device = Settings::values.vulkan_device.GetValue(true); 315 vulkan_device = Settings::values.vulkan_device.GetValue(true);
297 shader_backend = Settings::values.shader_backend.GetValue(true); 316 shader_backend = Settings::values.shader_backend.GetValue(true);
298 ui->device_widget->setEnabled(false); 317 ui->device_widget->setEnabled(false);
299 ui->backend_widget->setEnabled(false); 318 ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
300 } else { 319 } else {
301 vulkan_device = Settings::values.vulkan_device.GetValue(); 320 vulkan_device = Settings::values.vulkan_device.GetValue();
302 shader_backend = Settings::values.shader_backend.GetValue(); 321 shader_backend = Settings::values.shader_backend.GetValue();
@@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() {
318 } 337 }
319} 338}
320 339
321void ConfigureGraphics::RetrieveVulkanDevices() try { 340bool ConfigureGraphics::RetrieveVulkanDevices() try {
341 if (UISettings::values.has_broken_vulkan) {
342 return false;
343 }
344
322 using namespace Vulkan; 345 using namespace Vulkan;
323 346
324 vk::InstanceDispatch dld; 347 vk::InstanceDispatch dld;
@@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
333 vulkan_devices.push_back(QString::fromStdString(name)); 356 vulkan_devices.push_back(QString::fromStdString(name));
334 } 357 }
335 358
359 return true;
336} catch (const Vulkan::vk::Exception& exception) { 360} catch (const Vulkan::vk::Exception& exception) {
337 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 361 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
362 return false;
338} 363}
339 364
340Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 365Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
415 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 440 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
416 ConfigurationShared::InsertGlobalItem( 441 ConfigurationShared::InsertGlobalItem(
417 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 442 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
443
444 if (UISettings::values.has_broken_vulkan) {
445 ui->backend_widget->setEnabled(true);
446 ConfigurationShared::SetColoredComboBox(
447 ui->backend, ui->backend_widget,
448 static_cast<int>(Settings::values.shader_backend.GetValue(true)));
449 }
418} 450}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 1b101c940..8438f0187 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -41,7 +41,7 @@ private:
41 void UpdateDeviceSelection(int device); 41 void UpdateDeviceSelection(int device);
42 void UpdateShaderBackendSelection(int backend); 42 void UpdateShaderBackendSelection(int backend);
43 43
44 void RetrieveVulkanDevices(); 44 bool RetrieveVulkanDevices();
45 45
46 void SetupPerGameUI(); 46 void SetupPerGameUI();
47 47
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 74f0e0b79..2f94c94bc 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>437</width> 9 <width>471</width>
10 <height>482</height> 10 <height>759</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -171,11 +171,11 @@
171 </widget> 171 </widget>
172 </item> 172 </item>
173 <item> 173 <item>
174 <widget class="QCheckBox" name="accelerate_astc"> 174 <widget class="QCheckBox" name="accelerate_astc">
175 <property name="text"> 175 <property name="text">
176 <string>Accelerate ASTC texture decoding</string> 176 <string>Accelerate ASTC texture decoding</string>
177 </property> 177 </property>
178 </widget> 178 </widget>
179 </item> 179 </item>
180 <item> 180 <item>
181 <widget class="QWidget" name="nvdec_emulation_widget" native="true"> 181 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
@@ -438,43 +438,43 @@
438 </widget> 438 </widget>
439 </item> 439 </item>
440 <item> 440 <item>
441 <widget class="QWidget" name="anti_aliasing_layout" native="true"> 441 <widget class="QWidget" name="anti_aliasing_layout" native="true">
442 <layout class="QHBoxLayout" name="horizontalLayout_7"> 442 <layout class="QHBoxLayout" name="horizontalLayout_7">
443 <property name="leftMargin"> 443 <property name="leftMargin">
444 <number>0</number> 444 <number>0</number>
445 </property> 445 </property>
446 <property name="topMargin"> 446 <property name="topMargin">
447 <number>0</number> 447 <number>0</number>
448 </property> 448 </property>
449 <property name="rightMargin"> 449 <property name="rightMargin">
450 <number>0</number> 450 <number>0</number>
451 </property>
452 <property name="bottomMargin">
453 <number>0</number>
454 </property>
455 <item>
456 <widget class="QLabel" name="anti_aliasing_label">
457 <property name="text">
458 <string>Anti-Aliasing Method:</string>
459 </property>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
451 </property> 467 </property>
452 <property name="bottomMargin"> 468 </item>
453 <number>0</number> 469 <item>
470 <property name="text">
471 <string>FXAA</string>
454 </property> 472 </property>
455 <item> 473 </item>
456 <widget class="QLabel" name="anti_aliasing_label"> 474 </widget>
457 <property name="text"> 475 </item>
458 <string>Anti-Aliasing Method:</string> 476 </layout>
459 </property> 477 </widget>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
467 </property>
468 </item>
469 <item>
470 <property name="text">
471 <string>FXAA</string>
472 </property>
473 </item>
474 </widget>
475 </item>
476 </layout>
477 </widget>
478 </item> 478 </item>
479 <item> 479 <item>
480 <widget class="QWidget" name="bg_layout" native="true"> 480 <widget class="QWidget" name="bg_layout" native="true">
@@ -574,6 +574,13 @@
574 </property> 574 </property>
575 </spacer> 575 </spacer>
576 </item> 576 </item>
577 <item>
578 <widget class="QPushButton" name="button_check_vulkan">
579 <property name="text">
580 <string>Check for Working Vulkan</string>
581 </property>
582 </widget>
583 </item>
577 </layout> 584 </layout>
578 </widget> 585 </widget>
579 <resources/> 586 <resources/>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6679e9c53..edf0893c4 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
61 61
62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
63 for (const auto& group : registry.hotkey_groups) { 63 for (const auto& group : registry.hotkey_groups) {
64 auto* parent_item = new QStandardItem(group.first); 64 auto* parent_item =
65 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
65 parent_item->setEditable(false); 66 parent_item->setEditable(false);
67 parent_item->setData(group.first);
66 for (const auto& hotkey : group.second) { 68 for (const auto& hotkey : group.second) {
67 auto* action = new QStandardItem(hotkey.first); 69 auto* action =
70 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
68 auto* keyseq = 71 auto* keyseq =
69 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 72 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
70 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 73 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
71 action->setEditable(false); 74 action->setEditable(false);
75 action->setData(hotkey.first);
72 keyseq->setEditable(false); 76 keyseq->setEditable(false);
73 controller_keyseq->setEditable(false); 77 controller_keyseq->setEditable(false);
74 parent_item->appendRow({action, keyseq, controller_keyseq}); 78 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
93 ui->retranslateUi(this); 97 ui->retranslateUi(this);
94 98
95 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); 99 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
100 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
101 QStandardItem* parent = model->item(key_id, 0);
102 parent->setText(
103 QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
104 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
105 QStandardItem* action = parent->child(key_column_id, name_column);
106 action->setText(
107 QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
108 }
109 }
96} 110}
97 111
98void ConfigureHotkeys::Configure(QModelIndex index) { 112void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
273 const QStandardItem* controller_keyseq = 287 const QStandardItem* controller_keyseq =
274 parent->child(key_column_id, controller_column); 288 parent->child(key_column_id, controller_column);
275 for (auto& [group, sub_actions] : registry.hotkey_groups) { 289 for (auto& [group, sub_actions] : registry.hotkey_groups) {
276 if (group != parent->text()) 290 if (group != parent->data())
277 continue; 291 continue;
278 for (auto& [action_name, hotkey] : sub_actions) { 292 for (auto& [action_name, hotkey] : sub_actions) {
279 if (action_name != action->text()) 293 if (action_name != action->data())
280 continue; 294 continue;
281 hotkey.keyseq = QKeySequence(keyseq->text()); 295 hotkey.keyseq = QKeySequence(keyseq->text());
282 hotkey.controller_keyseq = controller_keyseq->text(); 296 hotkey.controller_keyseq = controller_keyseq->text();
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 27559c37b..c313b0919 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() {
151 &ConfigureMotionTouch::OnConfigureTouchCalibration); 151 &ConfigureMotionTouch::OnConfigureTouchCalibration);
152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, 152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
153 &ConfigureMotionTouch::OnConfigureTouchFromButton); 153 &ConfigureMotionTouch::OnConfigureTouchFromButton);
154 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
155 &ConfigureMotionTouch::ApplyConfiguration);
154 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { 156 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
155 if (CanCloseDialog()) { 157 if (CanCloseDialog()) {
156 reject(); 158 reject();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index c75a84ae4..0237fae54 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -293,22 +293,5 @@
293 </layout> 293 </layout>
294 </widget> 294 </widget>
295 <resources/> 295 <resources/>
296 <connections> 296 <connections/>
297 <connection>
298 <sender>buttonBox</sender>
299 <signal>accepted()</signal>
300 <receiver>ConfigureMotionTouch</receiver>
301 <slot>ApplyConfiguration()</slot>
302 <hints>
303 <hint type="sourcelabel">
304 <x>20</x>
305 <y>20</y>
306 </hint>
307 <hint type="destinationlabel">
308 <x>20</x>
309 <y>20</y>
310 </hint>
311 </hints>
312 </connection>
313 </connections>
314</ui> 297</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 19aa589f9..ecebb0fb7 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() {
130 // Guard if during game and set to game-specific value 130 // Guard if during game and set to game-specific value
131 if (Settings::values.rng_seed.UsingGlobal()) { 131 if (Settings::values.rng_seed.UsingGlobal()) {
132 if (ui->rng_seed_checkbox->isChecked()) { 132 if (ui->rng_seed_checkbox->isChecked()) {
133 Settings::values.rng_seed.SetValue( 133 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
134 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
135 } else { 134 } else {
136 Settings::values.rng_seed.SetValue(std::nullopt); 135 Settings::values.rng_seed.SetValue(std::nullopt);
137 } 136 }
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
142 case ConfigurationShared::CheckState::Off: 141 case ConfigurationShared::CheckState::Off:
143 Settings::values.rng_seed.SetGlobal(false); 142 Settings::values.rng_seed.SetGlobal(false);
144 if (ui->rng_seed_checkbox->isChecked()) { 143 if (ui->rng_seed_checkbox->isChecked()) {
145 Settings::values.rng_seed.SetValue( 144 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
146 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
147 } else { 145 } else {
148 Settings::values.rng_seed.SetValue(std::nullopt); 146 Settings::values.rng_seed.SetValue(std::nullopt);
149 } 147 }
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 4a6d74a7e..6321afc83 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) {
483 // Also artificially caps the watcher to a certain number of directories 483 // Also artificially caps the watcher to a certain number of directories
484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000; 484 constexpr int LIMIT_WATCH_DIRECTORIES = 5000;
485 constexpr int SLICE_SIZE = 25; 485 constexpr int SLICE_SIZE = 25;
486 int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); 486 int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES);
487 for (int i = 0; i < len; i += SLICE_SIZE) { 487 for (int i = 0; i < len; i += SLICE_SIZE) {
488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); 488 watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE));
489 QCoreApplication::processEvents(); 489 QCoreApplication::processEvents();
@@ -870,7 +870,7 @@ GameListPlaceholder::GameListPlaceholder(GMainWindow* parent) : QWidget{parent}
870 layout->setAlignment(Qt::AlignCenter); 870 layout->setAlignment(Qt::AlignCenter);
871 image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200)); 871 image->setPixmap(QIcon::fromTheme(QStringLiteral("plus_folder")).pixmap(200));
872 872
873 text->setText(tr("Double-click to add a new folder to the game list")); 873 RetranslateUI();
874 QFont font = text->font(); 874 QFont font = text->font();
875 font.setPointSize(20); 875 font.setPointSize(20);
876 text->setFont(font); 876 text->setFont(font);
@@ -891,3 +891,15 @@ void GameListPlaceholder::onUpdateThemedIcons() {
891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) { 891void GameListPlaceholder::mouseDoubleClickEvent(QMouseEvent* event) {
892 emit GameListPlaceholder::AddDirectory(); 892 emit GameListPlaceholder::AddDirectory();
893} 893}
894
895void GameListPlaceholder::changeEvent(QEvent* event) {
896 if (event->type() == QEvent::LanguageChange) {
897 RetranslateUI();
898 }
899
900 QWidget::changeEvent(event);
901}
902
903void GameListPlaceholder::RetranslateUI() {
904 text->setText(tr("Double-click to add a new folder to the game list"));
905}
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index d19dbe4b0..464da98ad 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -166,6 +166,9 @@ protected:
166 void mouseDoubleClickEvent(QMouseEvent* event) override; 166 void mouseDoubleClickEvent(QMouseEvent* event) override;
167 167
168private: 168private:
169 void changeEvent(QEvent* event) override;
170 void RetranslateUI();
171
169 QVBoxLayout* layout = nullptr; 172 QVBoxLayout* layout = nullptr;
170 QLabel* image = nullptr; 173 QLabel* image = nullptr;
171 QLabel* text = nullptr; 174 QLabel* text = nullptr;
diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp
index edfb946a8..e273744fd 100644
--- a/src/yuzu/loading_screen.cpp
+++ b/src/yuzu/loading_screen.cpp
@@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
183 183
184void LoadingScreen::paintEvent(QPaintEvent* event) { 184void LoadingScreen::paintEvent(QPaintEvent* event) {
185 QStyleOption opt; 185 QStyleOption opt;
186 opt.init(this); 186 opt.initFrom(this);
187 QPainter p(this); 187 QPainter p(this);
188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); 188 style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
189 QWidget::paintEvent(event); 189 QWidget::paintEvent(event);
diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h
index 7c960ee72..17045595d 100644
--- a/src/yuzu/loading_screen.h
+++ b/src/yuzu/loading_screen.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <QString> 8#include <QString>
9#include <QWidget> 9#include <QWidget>
10#include <QtGlobal>
10 11
11#if !QT_CONFIG(movie) 12#if !QT_CONFIG(movie)
12#define YUZU_QT_MOVIE_MISSING 1 13#define YUZU_QT_MOVIE_MISSING 1
@@ -88,4 +89,6 @@ private:
88 std::size_t slow_shader_first_value = 0; 89 std::size_t slow_shader_first_value = 0;
89}; 90};
90 91
92#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
91Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); 93Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage);
94#endif
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 574e2d263..89b1d9824 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
115#include "video_core/shader_notify.h" 115#include "video_core/shader_notify.h"
116#include "yuzu/about_dialog.h" 116#include "yuzu/about_dialog.h"
117#include "yuzu/bootmanager.h" 117#include "yuzu/bootmanager.h"
118#include "yuzu/check_vulkan.h"
118#include "yuzu/compatdb.h" 119#include "yuzu/compatdb.h"
119#include "yuzu/compatibility_list.h" 120#include "yuzu/compatibility_list.h"
120#include "yuzu/configuration/config.h" 121#include "yuzu/configuration/config.h"
@@ -198,6 +199,31 @@ static void RemoveCachedContents() {
198 Common::FS::RemoveDirRecursively(offline_system_data); 199 Common::FS::RemoveDirRecursively(offline_system_data);
199} 200}
200 201
202static QString PrettyProductName() {
203#ifdef _WIN32
204 // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
205 // With that notation change they changed the registry key used to denote the current version
206 QSettings windows_registry(
207 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
208 QSettings::NativeFormat);
209 const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
210 if (release_id == QStringLiteral("2009")) {
211 const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
212 const QString display_version =
213 windows_registry.value(QStringLiteral("DisplayVersion")).toString();
214 const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
215 u32 version = 10;
216 if (current_build >= 22000) {
217 version = 11;
218 }
219 return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
220 .arg(QString::number(version), display_version, QString::number(current_build),
221 QString::number(ubr));
222 }
223#endif
224 return QSysInfo::prettyProductName();
225}
226
201GMainWindow::GMainWindow() 227GMainWindow::GMainWindow()
202 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 228 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
203 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 229 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -259,7 +285,7 @@ GMainWindow::GMainWindow()
259 } 285 }
260 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 286 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
261#endif 287#endif
262 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 288 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
263 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 289 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
264 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 290 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
265 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); 291 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -297,6 +323,23 @@ GMainWindow::GMainWindow()
297 323
298 MigrateConfigFiles(); 324 MigrateConfigFiles();
299 325
326 if (!CheckVulkan()) {
327 config->Save();
328
329 QMessageBox::warning(
330 this, tr("Broken Vulkan Installation Detected"),
331 tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
332 "href='https://yuzu-emu.org/wiki/faq/"
333 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
334 "instructions to fix the issue</a>."));
335 }
336 if (UISettings::values.has_broken_vulkan) {
337 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
338
339 renderer_status_button->setDisabled(true);
340 renderer_status_button->setChecked(false);
341 }
342
300#if defined(HAVE_SDL2) && !defined(_WIN32) 343#if defined(HAVE_SDL2) && !defined(_WIN32)
301 SDL_InitSubSystem(SDL_INIT_VIDEO); 344 SDL_InitSubSystem(SDL_INIT_VIDEO);
302 // SDL disables the screen saver by default, and setting the hint 345 // SDL disables the screen saver by default, and setting the hint
@@ -827,12 +870,11 @@ void GMainWindow::InitializeWidgets() {
827 870
828 // Setup Dock button 871 // Setup Dock button
829 dock_status_button = new QPushButton(); 872 dock_status_button = new QPushButton();
830 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 873 dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
831 dock_status_button->setFocusPolicy(Qt::NoFocus); 874 dock_status_button->setFocusPolicy(Qt::NoFocus);
832 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); 875 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
833 dock_status_button->setText(tr("DOCK"));
834 dock_status_button->setCheckable(true); 876 dock_status_button->setCheckable(true);
835 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 877 UpdateDockedButton();
836 statusBar()->insertPermanentWidget(0, dock_status_button); 878 statusBar()->insertPermanentWidget(0, dock_status_button);
837 879
838 gpu_accuracy_button = new QPushButton(); 880 gpu_accuracy_button = new QPushButton();
@@ -1015,6 +1057,10 @@ void GMainWindow::SetDefaultUIGeometry() {
1015void GMainWindow::RestoreUIState() { 1057void GMainWindow::RestoreUIState() {
1016 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); 1058 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
1017 restoreGeometry(UISettings::values.geometry); 1059 restoreGeometry(UISettings::values.geometry);
1060 // Work-around because the games list isn't supposed to be full screen
1061 if (isFullScreen()) {
1062 showNormal();
1063 }
1018 restoreState(UISettings::values.state); 1064 restoreState(UISettings::values.state);
1019 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); 1065 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
1020 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 1066 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -1563,7 +1609,7 @@ void GMainWindow::ShutdownGame() {
1563 emu_speed_label->setVisible(false); 1609 emu_speed_label->setVisible(false);
1564 game_fps_label->setVisible(false); 1610 game_fps_label->setVisible(false);
1565 emu_frametime_label->setVisible(false); 1611 emu_frametime_label->setVisible(false);
1566 renderer_status_button->setEnabled(true); 1612 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
1567 1613
1568 game_path.clear(); 1614 game_path.clear();
1569 1615
@@ -1583,7 +1629,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
1583 1629
1584void GMainWindow::UpdateRecentFiles() { 1630void GMainWindow::UpdateRecentFiles() {
1585 const int num_recent_files = 1631 const int num_recent_files =
1586 std::min(UISettings::values.recent_files.size(), max_recent_files_item); 1632 std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item);
1587 1633
1588 for (int i = 0; i < num_recent_files; i++) { 1634 for (int i = 0; i < num_recent_files; i++) {
1589 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( 1635 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2785,6 +2831,10 @@ void GMainWindow::OnConfigure() {
2785 mouse_hide_timer.start(); 2831 mouse_hide_timer.start();
2786 } 2832 }
2787 2833
2834 if (!UISettings::values.has_broken_vulkan) {
2835 renderer_status_button->setEnabled(!emulation_running);
2836 }
2837
2788 UpdateStatusButtons(); 2838 UpdateStatusButtons();
2789 controller_dialog->refreshConfiguration(); 2839 controller_dialog->refreshConfiguration();
2790} 2840}
@@ -2870,7 +2920,7 @@ void GMainWindow::OnToggleDockedMode() {
2870 } 2920 }
2871 2921
2872 Settings::values.use_docked_mode.SetValue(!is_docked); 2922 Settings::values.use_docked_mode.SetValue(!is_docked);
2873 dock_status_button->setChecked(!is_docked); 2923 UpdateDockedButton();
2874 OnDockedModeChanged(is_docked, !is_docked, *system); 2924 OnDockedModeChanged(is_docked, !is_docked, *system);
2875} 2925}
2876 2926
@@ -3164,7 +3214,7 @@ void GMainWindow::OnTasStateChanged() {
3164} 3214}
3165 3215
3166void GMainWindow::UpdateStatusBar() { 3216void GMainWindow::UpdateStatusBar() {
3167 if (emu_thread == nullptr) { 3217 if (emu_thread == nullptr || !system->IsPoweredOn()) {
3168 status_bar_update_timer.stop(); 3218 status_bar_update_timer.stop();
3169 return; 3219 return;
3170 } 3220 }
@@ -3236,6 +3286,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3236 } 3286 }
3237} 3287}
3238 3288
3289void GMainWindow::UpdateDockedButton() {
3290 const bool is_docked = Settings::values.use_docked_mode.GetValue();
3291 dock_status_button->setChecked(is_docked);
3292 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3293}
3294
3239void GMainWindow::UpdateFilterText() { 3295void GMainWindow::UpdateFilterText() {
3240 const auto filter = Settings::values.scaling_filter.GetValue(); 3296 const auto filter = Settings::values.scaling_filter.GetValue();
3241 switch (filter) { 3297 switch (filter) {
@@ -3279,10 +3335,10 @@ void GMainWindow::UpdateAAText() {
3279} 3335}
3280 3336
3281void GMainWindow::UpdateStatusButtons() { 3337void GMainWindow::UpdateStatusButtons() {
3282 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
3283 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3338 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3284 Settings::RendererBackend::Vulkan); 3339 Settings::RendererBackend::Vulkan);
3285 UpdateGPUAccuracyButton(); 3340 UpdateGPUAccuracyButton();
3341 UpdateDockedButton();
3286 UpdateFilterText(); 3342 UpdateFilterText();
3287 UpdateAAText(); 3343 UpdateAAText();
3288} 3344}
@@ -3333,7 +3389,7 @@ void GMainWindow::CenterMouseCursor() {
3333 const int center_x = render_window->width() / 2; 3389 const int center_x = render_window->width() / 2;
3334 const int center_y = render_window->height() / 2; 3390 const int center_y = render_window->height() / 2;
3335 3391
3336 QCursor::setPos(mapToGlobal({center_x, center_y})); 3392 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
3337} 3393}
3338 3394
3339void GMainWindow::OnMouseActivity() { 3395void GMainWindow::OnMouseActivity() {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b399e9b01..600647015 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -320,6 +320,7 @@ private:
320 void MigrateConfigFiles(); 320 void MigrateConfigFiles();
321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, 321 void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
322 std::string_view gpu_vendor = {}); 322 std::string_view gpu_vendor = {});
323 void UpdateDockedButton();
323 void UpdateFilterText(); 324 void UpdateFilterText();
324 void UpdateAAText(); 325 void UpdateAAText();
325 void UpdateStatusBar(); 326 void UpdateStatusBar();
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 15ba9ea17..c64d87ace 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -77,6 +77,8 @@ struct Values {
77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; 79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
80 // Set when Vulkan is known to crash the application
81 Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
80 82
81 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; 83 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
82 84
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f34d6b728..39063e32b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor =
218 218
219[Renderer] 219[Renderer]
220# Which backend API to use. 220# Which backend API to use.
221# 0 (default): OpenGL, 1: Vulkan 221# 0: OpenGL, 1 (default): Vulkan
222backend = 222backend =
223 223
224# Enable graphics API debugging mode. 224# Enable graphics API debugging mode.