summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/elf.h333
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h2
-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/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/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/yuzu/CMakeLists.txt22
-rw-r--r--src/yuzu/bootmanager.cpp13
-rw-r--r--src/yuzu/bootmanager.h12
-rw-r--r--src/yuzu/configuration/config.cpp49
-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_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.cpp2
-rw-r--r--src/yuzu/loading_screen.cpp2
-rw-r--r--src/yuzu/loading_screen.h3
-rw-r--r--src/yuzu/main.cpp52
-rw-r--r--src/yuzu/main.h1
59 files changed, 3486 insertions, 825 deletions
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/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 6d6e50425..a507744a2 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -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/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/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/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 39989885d..9259ca15e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -189,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
189 # Update source TS file if enabled 189 # Update source TS file if enabled
190 if (GENERATE_QT_TRANSLATION) 190 if (GENERATE_QT_TRANSLATION)
191 get_target_property(SRCS yuzu SOURCES) 191 get_target_property(SRCS yuzu SOURCES)
192 qt5_create_translation(QM_FILES 192 qt_create_translation(QM_FILES
193 ${SRCS} 193 ${SRCS}
194 ${UIS} 194 ${UIS}
195 ${YUZU_QT_LANGUAGES}/en.ts 195 ${YUZU_QT_LANGUAGES}/en.ts
@@ -205,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) 205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
206 206
207 # Compile TS files to QM files 207 # Compile TS files to QM files
208 qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 208 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
209 209
210 # Build a QRC file from the QM file list 210 # Build a QRC file from the QM file list
211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -217,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") 217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
218 218
219 # Add the QRC file to package in all QM files 219 # Add the QRC file to package in all QM files
220 qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) 220 qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
221else() 221else()
222 set(LANGUAGES) 222 set(LANGUAGES)
223endif() 223endif()
@@ -238,7 +238,11 @@ if (APPLE)
238 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)
239elseif(WIN32) 239elseif(WIN32)
240 # compile as a win32 gui application instead of a console application 240 # compile as a win32 gui application instead of a console application
241 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()
242 if(MSVC) 246 if(MSVC)
243 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") 247 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
244 elseif(MINGW) 248 elseif(MINGW)
@@ -249,7 +253,7 @@ endif()
249create_target_directory_groups(yuzu) 253create_target_directory_groups(yuzu)
250 254
251target_link_libraries(yuzu PRIVATE common core input_common video_core) 255target_link_libraries(yuzu PRIVATE common core input_common video_core)
252target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) 256target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
253target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 257target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
254 258
255target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 259target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -257,7 +261,7 @@ if (NOT WIN32)
257 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 261 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
258endif() 262endif()
259if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 263if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
260 target_link_libraries(yuzu PRIVATE Qt5::DBus) 264 target_link_libraries(yuzu PRIVATE Qt::DBus)
261endif() 265endif()
262 266
263target_compile_definitions(yuzu PRIVATE 267target_compile_definitions(yuzu PRIVATE
@@ -293,7 +297,7 @@ if (USE_DISCORD_PRESENCE)
293endif() 297endif()
294 298
295if (YUZU_USE_QT_WEB_ENGINE) 299if (YUZU_USE_QT_WEB_ENGINE)
296 target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) 300 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
297 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 301 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
298endif () 302endif ()
299 303
@@ -321,3 +325,7 @@ endif()
321if (NOT APPLE) 325if (NOT APPLE)
322 target_compile_definitions(yuzu PRIVATE HAS_OPENGL) 326 target_compile_definitions(yuzu PRIVATE HAS_OPENGL)
323endif() 327endif()
328
329if (ARCHITECTURE_x86_64)
330 target_link_libraries(yuzu PRIVATE dynarmic)
331endif()
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/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8b95b677b..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);
@@ -1102,6 +1105,8 @@ void Config::SaveDebuggingValues() {
1102 1105
1103 // 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
1104 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);
1105 WriteBasicSetting(Settings::values.program_args); 1110 WriteBasicSetting(Settings::values.program_args);
1106 WriteBasicSetting(Settings::values.dump_exefs); 1111 WriteBasicSetting(Settings::values.dump_exefs);
1107 WriteBasicSetting(Settings::values.dump_nso); 1112 WriteBasicSetting(Settings::values.dump_nso);
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_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..d13530a5b 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();
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 d55733932..4d7634184 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -199,6 +199,31 @@ static void RemoveCachedContents() {
199 Common::FS::RemoveDirRecursively(offline_system_data); 199 Common::FS::RemoveDirRecursively(offline_system_data);
200} 200}
201 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
202GMainWindow::GMainWindow() 227GMainWindow::GMainWindow()
203 : 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>()},
204 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 229 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -260,7 +285,7 @@ GMainWindow::GMainWindow()
260 } 285 }
261 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 286 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
262#endif 287#endif
263 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 288 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
264 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 289 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
265 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 290 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
266 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});
@@ -845,12 +870,11 @@ void GMainWindow::InitializeWidgets() {
845 870
846 // Setup Dock button 871 // Setup Dock button
847 dock_status_button = new QPushButton(); 872 dock_status_button = new QPushButton();
848 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 873 dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
849 dock_status_button->setFocusPolicy(Qt::NoFocus); 874 dock_status_button->setFocusPolicy(Qt::NoFocus);
850 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); 875 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
851 dock_status_button->setText(tr("DOCK"));
852 dock_status_button->setCheckable(true); 876 dock_status_button->setCheckable(true);
853 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 877 UpdateDockedButton();
854 statusBar()->insertPermanentWidget(0, dock_status_button); 878 statusBar()->insertPermanentWidget(0, dock_status_button);
855 879
856 gpu_accuracy_button = new QPushButton(); 880 gpu_accuracy_button = new QPushButton();
@@ -1033,6 +1057,10 @@ void GMainWindow::SetDefaultUIGeometry() {
1033void GMainWindow::RestoreUIState() { 1057void GMainWindow::RestoreUIState() {
1034 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint); 1058 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
1035 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 }
1036 restoreState(UISettings::values.state); 1064 restoreState(UISettings::values.state);
1037 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint); 1065 render_window->setWindowFlags(render_window->windowFlags() & ~Qt::FramelessWindowHint);
1038 render_window->restoreGeometry(UISettings::values.renderwindow_geometry); 1066 render_window->restoreGeometry(UISettings::values.renderwindow_geometry);
@@ -1601,7 +1629,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
1601 1629
1602void GMainWindow::UpdateRecentFiles() { 1630void GMainWindow::UpdateRecentFiles() {
1603 const int num_recent_files = 1631 const int num_recent_files =
1604 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);
1605 1633
1606 for (int i = 0; i < num_recent_files; i++) { 1634 for (int i = 0; i < num_recent_files; i++) {
1607 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( 1635 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2880,7 +2908,7 @@ void GMainWindow::OnToggleDockedMode() {
2880 } 2908 }
2881 2909
2882 Settings::values.use_docked_mode.SetValue(!is_docked); 2910 Settings::values.use_docked_mode.SetValue(!is_docked);
2883 dock_status_button->setChecked(!is_docked); 2911 UpdateDockedButton();
2884 OnDockedModeChanged(is_docked, !is_docked, *system); 2912 OnDockedModeChanged(is_docked, !is_docked, *system);
2885} 2913}
2886 2914
@@ -3174,7 +3202,7 @@ void GMainWindow::OnTasStateChanged() {
3174} 3202}
3175 3203
3176void GMainWindow::UpdateStatusBar() { 3204void GMainWindow::UpdateStatusBar() {
3177 if (emu_thread == nullptr) { 3205 if (emu_thread == nullptr || !system->IsPoweredOn()) {
3178 status_bar_update_timer.stop(); 3206 status_bar_update_timer.stop();
3179 return; 3207 return;
3180 } 3208 }
@@ -3246,6 +3274,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3246 } 3274 }
3247} 3275}
3248 3276
3277void GMainWindow::UpdateDockedButton() {
3278 const bool is_docked = Settings::values.use_docked_mode.GetValue();
3279 dock_status_button->setChecked(is_docked);
3280 dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD"));
3281}
3282
3249void GMainWindow::UpdateFilterText() { 3283void GMainWindow::UpdateFilterText() {
3250 const auto filter = Settings::values.scaling_filter.GetValue(); 3284 const auto filter = Settings::values.scaling_filter.GetValue();
3251 switch (filter) { 3285 switch (filter) {
@@ -3289,10 +3323,10 @@ void GMainWindow::UpdateAAText() {
3289} 3323}
3290 3324
3291void GMainWindow::UpdateStatusButtons() { 3325void GMainWindow::UpdateStatusButtons() {
3292 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
3293 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3326 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3294 Settings::RendererBackend::Vulkan); 3327 Settings::RendererBackend::Vulkan);
3295 UpdateGPUAccuracyButton(); 3328 UpdateGPUAccuracyButton();
3329 UpdateDockedButton();
3296 UpdateFilterText(); 3330 UpdateFilterText();
3297 UpdateAAText(); 3331 UpdateAAText();
3298} 3332}
@@ -3343,7 +3377,7 @@ void GMainWindow::CenterMouseCursor() {
3343 const int center_x = render_window->width() / 2; 3377 const int center_x = render_window->width() / 2;
3344 const int center_y = render_window->height() / 2; 3378 const int center_y = render_window->height() / 2;
3345 3379
3346 QCursor::setPos(mapToGlobal({center_x, center_y})); 3380 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
3347} 3381}
3348 3382
3349void GMainWindow::OnMouseActivity() { 3383void 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();