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.h2
-rw-r--r--src/core/arm/symbols.cpp85
-rw-r--r--src/core/debugger/debugger.cpp21
-rw-r--r--src/core/debugger/gdbstub.cpp199
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/hle/kernel/k_thread.cpp4
-rw-r--r--src/core/hle/kernel/k_thread.h10
-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/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/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_library.cpp4
-rw-r--r--src/yuzu/CMakeLists.txt20
-rw-r--r--src/yuzu/bootmanager.cpp2
-rw-r--r--src/yuzu/bootmanager.h2
-rw-r--r--src/yuzu/check_vulkan.cpp53
-rw-r--r--src/yuzu/check_vulkan.h6
-rw-r--r--src/yuzu/configuration/config.cpp52
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp36
-rw-r--r--src/yuzu/configuration/configure_graphics.h2
-rw-r--r--src/yuzu/configuration/configure_graphics.ui91
-rw-r--r--src/yuzu/configuration/configure_hotkeys.cpp22
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp2
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui19
-rw-r--r--src/yuzu/configuration/configure_system.cpp6
-rw-r--r--src/yuzu/game_list.cpp2
-rw-r--r--src/yuzu/loading_screen.cpp2
-rw-r--r--src/yuzu/loading_screen.h3
-rw-r--r--src/yuzu/main.cpp70
-rw-r--r--src/yuzu/main.h1
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/default_ini.h2
41 files changed, 1326 insertions, 472 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.h b/src/common/settings.h
index a7bbfb0da..a507744a2 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -496,7 +496,7 @@ struct Values {
496 496
497 // Renderer 497 // Renderer
498 RangedSetting<RendererBackend> renderer_backend{ 498 RangedSetting<RendererBackend> renderer_backend{
499 RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; 499 RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
500 BasicSetting<bool> renderer_debug{false, "debug"}; 500 BasicSetting<bool> renderer_debug{false, "debug"};
501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; 501 BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"};
502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; 502 BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"};
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/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 68ab33e46..8d64990ed 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -20,15 +20,16 @@ template <typename Readable, typename Buffer, typename Callback>
20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { 20static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
21 static_assert(std::is_trivial_v<Buffer>); 21 static_assert(std::is_trivial_v<Buffer>);
22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; 22 auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
23 r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) { 23 r.async_read_some(
24 if (!error.failed()) { 24 boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
25 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); 25 if (!error.failed()) {
26 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; 26 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
27 c(received_data); 27 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
28 } 28 c(received_data);
29 }
29 30
30 AsyncReceiveInto(r, buffer, c); 31 AsyncReceiveInto(r, buffer, c);
31 }); 32 });
32} 33}
33 34
34template <typename Readable, typename Buffer> 35template <typename Readable, typename Buffer>
@@ -65,7 +66,7 @@ public:
65 } 66 }
66 stopped = true; 67 stopped = true;
67 68
68 signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread))); 69 boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread)));
69 return true; 70 return true;
70 } 71 }
71 72
@@ -74,7 +75,7 @@ public:
74 } 75 }
75 76
76 void WriteToClient(std::span<const u8> data) override { 77 void WriteToClient(std::span<const u8> data) override {
77 client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes())); 78 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
78 } 79 }
79 80
80 void SetActiveThread(Kernel::KThread* thread) override { 81 void SetActiveThread(Kernel::KThread* thread) override {
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 0c36069a6..f52d78829 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -34,6 +34,65 @@ constexpr char GDB_STUB_REPLY_ERR[] = "E01";
34constexpr char GDB_STUB_REPLY_OK[] = "OK"; 34constexpr char GDB_STUB_REPLY_OK[] = "OK";
35constexpr char GDB_STUB_REPLY_EMPTY[] = ""; 35constexpr char GDB_STUB_REPLY_EMPTY[] = "";
36 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
37GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) 96GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
38 : DebuggerFrontend(backend_), system{system_} { 97 : DebuggerFrontend(backend_), system{system_} {
39 if (system.CurrentProcess()->Is64BitProcess()) { 98 if (system.CurrentProcess()->Is64BitProcess()) {
@@ -255,6 +314,80 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction
255 } 314 }
256} 315}
257 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
258static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { 391static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
259 switch (thread->GetWaitReasonForDebugging()) { 392 switch (thread->GetWaitReasonForDebugging()) {
260 case Kernel::ThreadWaitReasonForDebugging::Sleep: 393 case Kernel::ThreadWaitReasonForDebugging::Sleep:
@@ -289,6 +422,18 @@ static std::string GetThreadState(const Kernel::KThread* thread) {
289 } 422 }
290} 423}
291 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
292void GDBStub::HandleQuery(std::string_view command) { 437void GDBStub::HandleQuery(std::string_view command) {
293 if (command.starts_with("TStatus")) { 438 if (command.starts_with("TStatus")) {
294 // no tracepoint support 439 // no tracepoint support
@@ -297,18 +442,8 @@ void GDBStub::HandleQuery(std::string_view command) {
297 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" 442 SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
298 "vContSupported+;QStartNoAckMode+"); 443 "vContSupported+;QStartNoAckMode+");
299 } else if (command.starts_with("Xfer:features:read:target.xml:")) { 444 } else if (command.starts_with("Xfer:features:read:target.xml:")) {
300 const auto offset{command.substr(30)};
301 const auto amount{command.substr(command.find(',') + 1)};
302
303 const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))};
304 const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
305 const auto target_xml{arch->GetTargetXML()}; 445 const auto target_xml{arch->GetTargetXML()};
306 446 SendReply(PaginateBuffer(target_xml, command.substr(30)));
307 if (offset_val + amount_val > target_xml.size()) {
308 SendReply("l" + target_xml.substr(offset_val));
309 } else {
310 SendReply("m" + target_xml.substr(offset_val, amount_val));
311 }
312 } else if (command.starts_with("Offsets")) { 447 } else if (command.starts_with("Offsets")) {
313 Loader::AppLoader::Modules modules; 448 Loader::AppLoader::Modules modules;
314 system.GetAppLoader().ReadNSOModules(modules); 449 system.GetAppLoader().ReadNSOModules(modules);
@@ -321,6 +456,20 @@ void GDBStub::HandleQuery(std::string_view command) {
321 SendReply(fmt::format("TextSeg={:x}", 456 SendReply(fmt::format("TextSeg={:x}",
322 system.CurrentProcess()->PageTable().GetCodeRegionStart())); 457 system.CurrentProcess()->PageTable().GetCodeRegionStart()));
323 } 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)));
324 } else if (command.starts_with("fThreadInfo")) { 473 } else if (command.starts_with("fThreadInfo")) {
325 // beginning of list 474 // beginning of list
326 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 475 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
@@ -332,20 +481,26 @@ void GDBStub::HandleQuery(std::string_view command) {
332 } else if (command.starts_with("sThreadInfo")) { 481 } else if (command.starts_with("sThreadInfo")) {
333 // end of list 482 // end of list
334 SendReply("l"); 483 SendReply("l");
335 } else if (command.starts_with("Xfer:threads:read")) { 484 } else if (command.starts_with("Xfer:threads:read::")) {
336 std::string buffer; 485 std::string buffer;
337 buffer += R"(l<?xml version="1.0"?>)"; 486 buffer += R"(<?xml version="1.0"?>)";
338 buffer += "<threads>"; 487 buffer += "<threads>";
339 488
340 const auto& threads = system.GlobalSchedulerContext().GetThreadList(); 489 const auto& threads = system.GlobalSchedulerContext().GetThreadList();
341 for (const auto& thread : threads) { 490 for (const auto* thread : threads) {
342 buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", 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>)",
343 thread->GetThreadID(), thread->GetActiveCore(), 497 thread->GetThreadID(), thread->GetActiveCore(),
344 thread->GetThreadID(), GetThreadState(thread)); 498 EscapeXML(*thread_name), GetThreadState(thread));
345 } 499 }
346 500
347 buffer += "</threads>"; 501 buffer += "</threads>";
348 SendReply(buffer); 502
503 SendReply(PaginateBuffer(buffer, command.substr(19)));
349 } else if (command.starts_with("Attached")) { 504 } else if (command.starts_with("Attached")) {
350 SendReply("0"); 505 SendReply("0");
351 } else if (command.starts_with("StartNoAckMode")) { 506 } else if (command.starts_with("StartNoAckMode")) {
@@ -438,14 +593,10 @@ std::optional<std::string> GDBStub::DetachCommand() {
438 return data.substr(1, data.size() - 4); 593 return data.substr(1, data.size() - 4);
439} 594}
440 595
441u8 GDBStub::CalculateChecksum(std::string_view data) {
442 return std::accumulate(data.begin(), data.end(), u8{0},
443 [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
444}
445
446void GDBStub::SendReply(std::string_view data) { 596void GDBStub::SendReply(std::string_view data) {
447 const auto output{ 597 const auto escaped{EscapeGDB(data)};
448 fmt::format("{}{}{}{:02x}", GDB_STUB_START, data, GDB_STUB_END, CalculateChecksum(data))}; 598 const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
599 CalculateChecksum(escaped))};
449 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); 600 LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
450 601
451 // C++ string support is complete rubbish 602 // C++ string support is complete rubbish
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index aa1f7de6c..1bb638187 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -34,7 +34,6 @@ private:
34 std::optional<std::string> DetachCommand(); 34 std::optional<std::string> DetachCommand();
35 Kernel::KThread* GetThreadByID(u64 thread_id); 35 Kernel::KThread* GetThreadByID(u64 thread_id);
36 36
37 static u8 CalculateChecksum(std::string_view data);
38 void SendReply(std::string_view data); 37 void SendReply(std::string_view data);
39 void SendStatus(char status); 38 void SendStatus(char status);
40 39
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 60ae0da78..f4d83f99a 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -660,6 +660,14 @@ public:
660 void IfDummyThreadTryWait(); 660 void IfDummyThreadTryWait();
661 void IfDummyThreadEndWait(); 661 void IfDummyThreadEndWait();
662 662
663 [[nodiscard]] uintptr_t GetArgument() const {
664 return argument;
665 }
666
667 [[nodiscard]] VAddr GetUserStackTop() const {
668 return stack_top;
669 }
670
663private: 671private:
664 static constexpr size_t PriorityInheritanceCountMax = 10; 672 static constexpr size_t PriorityInheritanceCountMax = 10;
665 union SyncObjectBuffer { 673 union SyncObjectBuffer {
@@ -791,6 +799,8 @@ private:
791 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 799 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
792 VAddr mutex_wait_address_for_debugging{}; 800 VAddr mutex_wait_address_for_debugging{};
793 ThreadWaitReasonForDebugging wait_reason_for_debugging{}; 801 ThreadWaitReasonForDebugging wait_reason_for_debugging{};
802 uintptr_t argument;
803 VAddr stack_top;
794 804
795public: 805public:
796 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/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/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 7d0cb8fce..3a4646289 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -595,8 +595,8 @@ void Maxwell3D::DrawArrays() {
595 595
596std::optional<u64> Maxwell3D::GetQueryResult() { 596std::optional<u64> Maxwell3D::GetQueryResult() {
597 switch (regs.query.query_get.select) { 597 switch (regs.query.query_get.select) {
598 case Regs::QuerySelect::Zero: 598 case Regs::QuerySelect::Payload:
599 return 0; 599 return regs.query.query_sequence;
600 case Regs::QuerySelect::SamplesPassed: 600 case Regs::QuerySelect::SamplesPassed:
601 // Deferred. 601 // Deferred.
602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, 602 rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed,
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index c0c2c7d96..434ba0877 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -93,7 +93,7 @@ public:
93 }; 93 };
94 94
95 enum class QuerySelect : u32 { 95 enum class QuerySelect : u32 {
96 Zero = 0, 96 Payload = 0,
97 TimeElapsed = 2, 97 TimeElapsed = 2,
98 TransformFeedbackPrimitivesGenerated = 11, 98 TransformFeedbackPrimitivesGenerated = 11,
99 PrimitivesGenerated = 18, 99 PrimitivesGenerated = 18,
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp
index a5dd33fb2..4eb3913ee 100644
--- a/src/video_core/vulkan_common/vulkan_library.cpp
+++ b/src/video_core/vulkan_common/vulkan_library.cpp
@@ -5,11 +5,13 @@
5 5
6#include "common/dynamic_library.h" 6#include "common/dynamic_library.h"
7#include "common/fs/path_util.h" 7#include "common/fs/path_util.h"
8#include "common/logging/log.h"
8#include "video_core/vulkan_common/vulkan_library.h" 9#include "video_core/vulkan_common/vulkan_library.h"
9 10
10namespace Vulkan { 11namespace Vulkan {
11 12
12Common::DynamicLibrary OpenLibrary() { 13Common::DynamicLibrary OpenLibrary() {
14 LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library");
13 Common::DynamicLibrary library; 15 Common::DynamicLibrary library;
14#ifdef __APPLE__ 16#ifdef __APPLE__
15 // Check if a path to a specific Vulkan library has been specified. 17 // Check if a path to a specific Vulkan library has been specified.
@@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() {
22 } 24 }
23#else 25#else
24 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); 26 std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1);
27 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename);
25 if (!library.Open(filename.c_str())) { 28 if (!library.Open(filename.c_str())) {
26 // Android devices may not have libvulkan.so.1, only libvulkan.so. 29 // Android devices may not have libvulkan.so.1, only libvulkan.so.
27 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); 30 filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
31 LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename);
28 void(library.Open(filename.c_str())); 32 void(library.Open(filename.c_str()));
29 } 33 }
30#endif 34#endif
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 07df9675d..9259ca15e 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -30,6 +30,8 @@ add_executable(yuzu
30 applets/qt_web_browser_scripts.h 30 applets/qt_web_browser_scripts.h
31 bootmanager.cpp 31 bootmanager.cpp
32 bootmanager.h 32 bootmanager.h
33 check_vulkan.cpp
34 check_vulkan.h
33 compatdb.ui 35 compatdb.ui
34 compatibility_list.cpp 36 compatibility_list.cpp
35 compatibility_list.h 37 compatibility_list.h
@@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION)
187 # Update source TS file if enabled 189 # Update source TS file if enabled
188 if (GENERATE_QT_TRANSLATION) 190 if (GENERATE_QT_TRANSLATION)
189 get_target_property(SRCS yuzu SOURCES) 191 get_target_property(SRCS yuzu SOURCES)
190 qt5_create_translation(QM_FILES 192 qt_create_translation(QM_FILES
191 ${SRCS} 193 ${SRCS}
192 ${UIS} 194 ${UIS}
193 ${YUZU_QT_LANGUAGES}/en.ts 195 ${YUZU_QT_LANGUAGES}/en.ts
@@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION)
203 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) 205 list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
204 206
205 # Compile TS files to QM files 207 # Compile TS files to QM files
206 qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) 208 qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
207 209
208 # Build a QRC file from the QM file list 210 # Build a QRC file from the QM file list
209 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) 211 set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
@@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION)
215 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") 217 file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
216 218
217 # Add the QRC file to package in all QM files 219 # Add the QRC file to package in all QM files
218 qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) 220 qt_add_resources(LANGUAGES ${LANGUAGES_QRC})
219else() 221else()
220 set(LANGUAGES) 222 set(LANGUAGES)
221endif() 223endif()
@@ -236,7 +238,11 @@ if (APPLE)
236 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 238 set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
237elseif(WIN32) 239elseif(WIN32)
238 # compile as a win32 gui application instead of a console application 240 # compile as a win32 gui application instead of a console application
239 target_link_libraries(yuzu PRIVATE Qt5::WinMain) 241 if (QT_VERSION VERSION_GREATER 6)
242 target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate)
243 else()
244 target_link_libraries(yuzu PRIVATE Qt5::WinMain)
245 endif()
240 if(MSVC) 246 if(MSVC)
241 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") 247 set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
242 elseif(MINGW) 248 elseif(MINGW)
@@ -247,7 +253,7 @@ endif()
247create_target_directory_groups(yuzu) 253create_target_directory_groups(yuzu)
248 254
249target_link_libraries(yuzu PRIVATE common core input_common video_core) 255target_link_libraries(yuzu PRIVATE common core input_common video_core)
250target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) 256target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
251target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) 257target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
252 258
253target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) 259target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
@@ -255,7 +261,7 @@ if (NOT WIN32)
255 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) 261 target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
256endif() 262endif()
257if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 263if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
258 target_link_libraries(yuzu PRIVATE Qt5::DBus) 264 target_link_libraries(yuzu PRIVATE Qt::DBus)
259endif() 265endif()
260 266
261target_compile_definitions(yuzu PRIVATE 267target_compile_definitions(yuzu PRIVATE
@@ -291,7 +297,7 @@ if (USE_DISCORD_PRESENCE)
291endif() 297endif()
292 298
293if (YUZU_USE_QT_WEB_ENGINE) 299if (YUZU_USE_QT_WEB_ENGINE)
294 target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) 300 target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets)
295 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 301 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
296endif () 302endif ()
297 303
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index aae2de2f8..bde465485 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -752,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
752 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);
753 753
754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { 754 if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
755 QCursor::setPos(mapToGlobal({center_x, center_y})); 755 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
756 } 756 }
757 757
758 emit MouseActivity(); 758 emit MouseActivity();
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 87c559e7a..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;
diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp
new file mode 100644
index 000000000..e6d66ab34
--- /dev/null
+++ b/src/yuzu/check_vulkan.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "video_core/vulkan_common/vulkan_wrapper.h"
5
6#include <filesystem>
7#include <fstream>
8#include "common/fs/fs.h"
9#include "common/fs/path_util.h"
10#include "common/logging/log.h"
11#include "video_core/vulkan_common/vulkan_instance.h"
12#include "video_core/vulkan_common/vulkan_library.h"
13#include "yuzu/check_vulkan.h"
14#include "yuzu/uisettings.h"
15
16constexpr char TEMP_FILE_NAME[] = "vulkan_check";
17
18bool CheckVulkan() {
19 if (UISettings::values.has_broken_vulkan) {
20 return true;
21 }
22
23 LOG_DEBUG(Frontend, "Checking presence of Vulkan");
24
25 const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
26 const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
27
28 if (std::filesystem::exists(temp_file_loc)) {
29 LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
30
31 UISettings::values.has_broken_vulkan = true;
32 std::filesystem::remove(temp_file_loc);
33 return false;
34 }
35
36 std::ofstream temp_file_handle(temp_file_loc);
37 temp_file_handle.close();
38
39 try {
40 Vulkan::vk::InstanceDispatch dld;
41 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
42 const Vulkan::vk::Instance instance =
43 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
44
45 } catch (const Vulkan::vk::Exception& exception) {
46 LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
47 // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
48 // application, not when we can handle it.
49 }
50
51 std::filesystem::remove(temp_file_loc);
52 return true;
53}
diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h
new file mode 100644
index 000000000..e4ea93582
--- /dev/null
+++ b/src/yuzu/check_vulkan.h
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6bool CheckVulkan();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 583e9df24..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
@@ -682,6 +682,12 @@ void Config::ReadRendererValues() {
682 ReadGlobalSetting(Settings::values.bg_green); 682 ReadGlobalSetting(Settings::values.bg_green);
683 ReadGlobalSetting(Settings::values.bg_blue); 683 ReadGlobalSetting(Settings::values.bg_blue);
684 684
685 if (!global && UISettings::values.has_broken_vulkan &&
686 Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
687 !Settings::values.renderer_backend.UsingGlobal()) {
688 Settings::values.renderer_backend.SetGlobal(true);
689 }
690
685 if (global) { 691 if (global) {
686 ReadBasicSetting(Settings::values.renderer_debug); 692 ReadBasicSetting(Settings::values.renderer_debug);
687 ReadBasicSetting(Settings::values.renderer_shader_feedback); 693 ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -801,6 +807,7 @@ void Config::ReadUIValues() {
801 ReadBasicSetting(UISettings::values.pause_when_in_background); 807 ReadBasicSetting(UISettings::values.pause_when_in_background);
802 ReadBasicSetting(UISettings::values.mute_when_in_background); 808 ReadBasicSetting(UISettings::values.mute_when_in_background);
803 ReadBasicSetting(UISettings::values.hide_mouse); 809 ReadBasicSetting(UISettings::values.hide_mouse);
810 ReadBasicSetting(UISettings::values.has_broken_vulkan);
804 ReadBasicSetting(UISettings::values.disable_web_applet); 811 ReadBasicSetting(UISettings::values.disable_web_applet);
805 812
806 qt_config->endGroup(); 813 qt_config->endGroup();
@@ -1348,6 +1355,7 @@ void Config::SaveUIValues() {
1348 WriteBasicSetting(UISettings::values.pause_when_in_background); 1355 WriteBasicSetting(UISettings::values.pause_when_in_background);
1349 WriteBasicSetting(UISettings::values.mute_when_in_background); 1356 WriteBasicSetting(UISettings::values.mute_when_in_background);
1350 WriteBasicSetting(UISettings::values.hide_mouse); 1357 WriteBasicSetting(UISettings::values.hide_mouse);
1358 WriteBasicSetting(UISettings::values.has_broken_vulkan);
1351 WriteBasicSetting(UISettings::values.disable_web_applet); 1359 WriteBasicSetting(UISettings::values.disable_web_applet);
1352 1360
1353 qt_config->endGroup(); 1361 qt_config->endGroup();
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 2f1435b10..85f34dc35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -17,6 +17,7 @@
17#include "video_core/vulkan_common/vulkan_library.h" 17#include "video_core/vulkan_common/vulkan_library.h"
18#include "yuzu/configuration/configuration_shared.h" 18#include "yuzu/configuration/configuration_shared.h"
19#include "yuzu/configuration/configure_graphics.h" 19#include "yuzu/configuration/configure_graphics.h"
20#include "yuzu/uisettings.h"
20 21
21ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) 22ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
22 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { 23 : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
@@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
57 UpdateBackgroundColorButton(new_bg_color); 58 UpdateBackgroundColorButton(new_bg_color);
58 }); 59 });
59 60
61 connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
62 UISettings::values.has_broken_vulkan = false;
63
64 if (RetrieveVulkanDevices()) {
65 ui->api->setEnabled(true);
66 ui->button_check_vulkan->hide();
67
68 for (const auto& device : vulkan_devices) {
69 ui->device->addItem(device);
70 }
71 } else {
72 UISettings::values.has_broken_vulkan = true;
73 }
74 });
75
76 ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
77 ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
78
60 ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); 79 ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
61 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); 80 ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
62} 81}
@@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
296 vulkan_device = Settings::values.vulkan_device.GetValue(true); 315 vulkan_device = Settings::values.vulkan_device.GetValue(true);
297 shader_backend = Settings::values.shader_backend.GetValue(true); 316 shader_backend = Settings::values.shader_backend.GetValue(true);
298 ui->device_widget->setEnabled(false); 317 ui->device_widget->setEnabled(false);
299 ui->backend_widget->setEnabled(false); 318 ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
300 } else { 319 } else {
301 vulkan_device = Settings::values.vulkan_device.GetValue(); 320 vulkan_device = Settings::values.vulkan_device.GetValue();
302 shader_backend = Settings::values.shader_backend.GetValue(); 321 shader_backend = Settings::values.shader_backend.GetValue();
@@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() {
318 } 337 }
319} 338}
320 339
321void ConfigureGraphics::RetrieveVulkanDevices() try { 340bool ConfigureGraphics::RetrieveVulkanDevices() try {
341 if (UISettings::values.has_broken_vulkan) {
342 return false;
343 }
344
322 using namespace Vulkan; 345 using namespace Vulkan;
323 346
324 vk::InstanceDispatch dld; 347 vk::InstanceDispatch dld;
@@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
333 vulkan_devices.push_back(QString::fromStdString(name)); 356 vulkan_devices.push_back(QString::fromStdString(name));
334 } 357 }
335 358
359 return true;
336} catch (const Vulkan::vk::Exception& exception) { 360} catch (const Vulkan::vk::Exception& exception) {
337 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); 361 LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
362 return false;
338} 363}
339 364
340Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { 365Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
415 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); 440 ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
416 ConfigurationShared::InsertGlobalItem( 441 ConfigurationShared::InsertGlobalItem(
417 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); 442 ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
443
444 if (UISettings::values.has_broken_vulkan) {
445 ui->backend_widget->setEnabled(true);
446 ConfigurationShared::SetColoredComboBox(
447 ui->backend, ui->backend_widget,
448 static_cast<int>(Settings::values.shader_backend.GetValue(true)));
449 }
418} 450}
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 1b101c940..8438f0187 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -41,7 +41,7 @@ private:
41 void UpdateDeviceSelection(int device); 41 void UpdateDeviceSelection(int device);
42 void UpdateShaderBackendSelection(int backend); 42 void UpdateShaderBackendSelection(int backend);
43 43
44 void RetrieveVulkanDevices(); 44 bool RetrieveVulkanDevices();
45 45
46 void SetupPerGameUI(); 46 void SetupPerGameUI();
47 47
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 74f0e0b79..2f94c94bc 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>437</width> 9 <width>471</width>
10 <height>482</height> 10 <height>759</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -171,11 +171,11 @@
171 </widget> 171 </widget>
172 </item> 172 </item>
173 <item> 173 <item>
174 <widget class="QCheckBox" name="accelerate_astc"> 174 <widget class="QCheckBox" name="accelerate_astc">
175 <property name="text"> 175 <property name="text">
176 <string>Accelerate ASTC texture decoding</string> 176 <string>Accelerate ASTC texture decoding</string>
177 </property> 177 </property>
178 </widget> 178 </widget>
179 </item> 179 </item>
180 <item> 180 <item>
181 <widget class="QWidget" name="nvdec_emulation_widget" native="true"> 181 <widget class="QWidget" name="nvdec_emulation_widget" native="true">
@@ -438,43 +438,43 @@
438 </widget> 438 </widget>
439 </item> 439 </item>
440 <item> 440 <item>
441 <widget class="QWidget" name="anti_aliasing_layout" native="true"> 441 <widget class="QWidget" name="anti_aliasing_layout" native="true">
442 <layout class="QHBoxLayout" name="horizontalLayout_7"> 442 <layout class="QHBoxLayout" name="horizontalLayout_7">
443 <property name="leftMargin"> 443 <property name="leftMargin">
444 <number>0</number> 444 <number>0</number>
445 </property> 445 </property>
446 <property name="topMargin"> 446 <property name="topMargin">
447 <number>0</number> 447 <number>0</number>
448 </property> 448 </property>
449 <property name="rightMargin"> 449 <property name="rightMargin">
450 <number>0</number> 450 <number>0</number>
451 </property>
452 <property name="bottomMargin">
453 <number>0</number>
454 </property>
455 <item>
456 <widget class="QLabel" name="anti_aliasing_label">
457 <property name="text">
458 <string>Anti-Aliasing Method:</string>
459 </property>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
451 </property> 467 </property>
452 <property name="bottomMargin"> 468 </item>
453 <number>0</number> 469 <item>
470 <property name="text">
471 <string>FXAA</string>
454 </property> 472 </property>
455 <item> 473 </item>
456 <widget class="QLabel" name="anti_aliasing_label"> 474 </widget>
457 <property name="text"> 475 </item>
458 <string>Anti-Aliasing Method:</string> 476 </layout>
459 </property> 477 </widget>
460 </widget>
461 </item>
462 <item>
463 <widget class="QComboBox" name="anti_aliasing_combobox">
464 <item>
465 <property name="text">
466 <string>None</string>
467 </property>
468 </item>
469 <item>
470 <property name="text">
471 <string>FXAA</string>
472 </property>
473 </item>
474 </widget>
475 </item>
476 </layout>
477 </widget>
478 </item> 478 </item>
479 <item> 479 <item>
480 <widget class="QWidget" name="bg_layout" native="true"> 480 <widget class="QWidget" name="bg_layout" native="true">
@@ -574,6 +574,13 @@
574 </property> 574 </property>
575 </spacer> 575 </spacer>
576 </item> 576 </item>
577 <item>
578 <widget class="QPushButton" name="button_check_vulkan">
579 <property name="text">
580 <string>Check for Working Vulkan</string>
581 </property>
582 </widget>
583 </item>
577 </layout> 584 </layout>
578 </widget> 585 </widget>
579 <resources/> 586 <resources/>
diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp
index 6679e9c53..edf0893c4 100644
--- a/src/yuzu/configuration/configure_hotkeys.cpp
+++ b/src/yuzu/configuration/configure_hotkeys.cpp
@@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default;
61 61
62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { 62void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) {
63 for (const auto& group : registry.hotkey_groups) { 63 for (const auto& group : registry.hotkey_groups) {
64 auto* parent_item = new QStandardItem(group.first); 64 auto* parent_item =
65 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first)));
65 parent_item->setEditable(false); 66 parent_item->setEditable(false);
67 parent_item->setData(group.first);
66 for (const auto& hotkey : group.second) { 68 for (const auto& hotkey : group.second) {
67 auto* action = new QStandardItem(hotkey.first); 69 auto* action =
70 new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first)));
68 auto* keyseq = 71 auto* keyseq =
69 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); 72 new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText));
70 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); 73 auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq);
71 action->setEditable(false); 74 action->setEditable(false);
75 action->setData(hotkey.first);
72 keyseq->setEditable(false); 76 keyseq->setEditable(false);
73 controller_keyseq->setEditable(false); 77 controller_keyseq->setEditable(false);
74 parent_item->appendRow({action, keyseq, controller_keyseq}); 78 parent_item->appendRow({action, keyseq, controller_keyseq});
@@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() {
93 ui->retranslateUi(this); 97 ui->retranslateUi(this);
94 98
95 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); 99 model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")});
100 for (int key_id = 0; key_id < model->rowCount(); key_id++) {
101 QStandardItem* parent = model->item(key_id, 0);
102 parent->setText(
103 QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString())));
104 for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) {
105 QStandardItem* action = parent->child(key_column_id, name_column);
106 action->setText(
107 QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString())));
108 }
109 }
96} 110}
97 111
98void ConfigureHotkeys::Configure(QModelIndex index) { 112void ConfigureHotkeys::Configure(QModelIndex index) {
@@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) {
273 const QStandardItem* controller_keyseq = 287 const QStandardItem* controller_keyseq =
274 parent->child(key_column_id, controller_column); 288 parent->child(key_column_id, controller_column);
275 for (auto& [group, sub_actions] : registry.hotkey_groups) { 289 for (auto& [group, sub_actions] : registry.hotkey_groups) {
276 if (group != parent->text()) 290 if (group != parent->data())
277 continue; 291 continue;
278 for (auto& [action_name, hotkey] : sub_actions) { 292 for (auto& [action_name, hotkey] : sub_actions) {
279 if (action_name != action->text()) 293 if (action_name != action->data())
280 continue; 294 continue;
281 hotkey.keyseq = QKeySequence(keyseq->text()); 295 hotkey.keyseq = QKeySequence(keyseq->text());
282 hotkey.controller_keyseq = controller_keyseq->text(); 296 hotkey.controller_keyseq = controller_keyseq->text();
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index 27559c37b..c313b0919 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() {
151 &ConfigureMotionTouch::OnConfigureTouchCalibration); 151 &ConfigureMotionTouch::OnConfigureTouchCalibration);
152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, 152 connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
153 &ConfigureMotionTouch::OnConfigureTouchFromButton); 153 &ConfigureMotionTouch::OnConfigureTouchFromButton);
154 connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
155 &ConfigureMotionTouch::ApplyConfiguration);
154 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { 156 connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
155 if (CanCloseDialog()) { 157 if (CanCloseDialog()) {
156 reject(); 158 reject();
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
index c75a84ae4..0237fae54 100644
--- a/src/yuzu/configuration/configure_motion_touch.ui
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -293,22 +293,5 @@
293 </layout> 293 </layout>
294 </widget> 294 </widget>
295 <resources/> 295 <resources/>
296 <connections> 296 <connections/>
297 <connection>
298 <sender>buttonBox</sender>
299 <signal>accepted()</signal>
300 <receiver>ConfigureMotionTouch</receiver>
301 <slot>ApplyConfiguration()</slot>
302 <hints>
303 <hint type="sourcelabel">
304 <x>20</x>
305 <y>20</y>
306 </hint>
307 <hint type="destinationlabel">
308 <x>20</x>
309 <y>20</y>
310 </hint>
311 </hints>
312 </connection>
313 </connections>
314</ui> 297</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 19aa589f9..ecebb0fb7 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() {
130 // Guard if during game and set to game-specific value 130 // Guard if during game and set to game-specific value
131 if (Settings::values.rng_seed.UsingGlobal()) { 131 if (Settings::values.rng_seed.UsingGlobal()) {
132 if (ui->rng_seed_checkbox->isChecked()) { 132 if (ui->rng_seed_checkbox->isChecked()) {
133 Settings::values.rng_seed.SetValue( 133 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
134 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
135 } else { 134 } else {
136 Settings::values.rng_seed.SetValue(std::nullopt); 135 Settings::values.rng_seed.SetValue(std::nullopt);
137 } 136 }
@@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() {
142 case ConfigurationShared::CheckState::Off: 141 case ConfigurationShared::CheckState::Off:
143 Settings::values.rng_seed.SetGlobal(false); 142 Settings::values.rng_seed.SetGlobal(false);
144 if (ui->rng_seed_checkbox->isChecked()) { 143 if (ui->rng_seed_checkbox->isChecked()) {
145 Settings::values.rng_seed.SetValue( 144 Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16));
146 ui->rng_seed_edit->text().toULongLong(nullptr, 16));
147 } else { 145 } else {
148 Settings::values.rng_seed.SetValue(std::nullopt); 146 Settings::values.rng_seed.SetValue(std::nullopt);
149 } 147 }
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 4a6d74a7e..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 8e7f91a0f..4d7634184 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
115#include "video_core/shader_notify.h" 115#include "video_core/shader_notify.h"
116#include "yuzu/about_dialog.h" 116#include "yuzu/about_dialog.h"
117#include "yuzu/bootmanager.h" 117#include "yuzu/bootmanager.h"
118#include "yuzu/check_vulkan.h"
118#include "yuzu/compatdb.h" 119#include "yuzu/compatdb.h"
119#include "yuzu/compatibility_list.h" 120#include "yuzu/compatibility_list.h"
120#include "yuzu/configuration/config.h" 121#include "yuzu/configuration/config.h"
@@ -198,6 +199,31 @@ static void RemoveCachedContents() {
198 Common::FS::RemoveDirRecursively(offline_system_data); 199 Common::FS::RemoveDirRecursively(offline_system_data);
199} 200}
200 201
202static QString PrettyProductName() {
203#ifdef _WIN32
204 // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2
205 // With that notation change they changed the registry key used to denote the current version
206 QSettings windows_registry(
207 QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
208 QSettings::NativeFormat);
209 const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString();
210 if (release_id == QStringLiteral("2009")) {
211 const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt();
212 const QString display_version =
213 windows_registry.value(QStringLiteral("DisplayVersion")).toString();
214 const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt();
215 u32 version = 10;
216 if (current_build >= 22000) {
217 version = 11;
218 }
219 return QStringLiteral("Windows %1 Version %2 (Build %3.%4)")
220 .arg(QString::number(version), display_version, QString::number(current_build),
221 QString::number(ubr));
222 }
223#endif
224 return QSysInfo::prettyProductName();
225}
226
201GMainWindow::GMainWindow() 227GMainWindow::GMainWindow()
202 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, 228 : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
203 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 229 input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
@@ -259,7 +285,7 @@ GMainWindow::GMainWindow()
259 } 285 }
260 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 286 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
261#endif 287#endif
262 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); 288 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
263 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 289 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
264 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); 290 Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB});
265 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); 291 LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB});
@@ -297,6 +323,23 @@ GMainWindow::GMainWindow()
297 323
298 MigrateConfigFiles(); 324 MigrateConfigFiles();
299 325
326 if (!CheckVulkan()) {
327 config->Save();
328
329 QMessageBox::warning(
330 this, tr("Broken Vulkan Installation Detected"),
331 tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
332 "href='https://yuzu-emu.org/wiki/faq/"
333 "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
334 "instructions to fix the issue</a>."));
335 }
336 if (UISettings::values.has_broken_vulkan) {
337 Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
338
339 renderer_status_button->setDisabled(true);
340 renderer_status_button->setChecked(false);
341 }
342
300#if defined(HAVE_SDL2) && !defined(_WIN32) 343#if defined(HAVE_SDL2) && !defined(_WIN32)
301 SDL_InitSubSystem(SDL_INIT_VIDEO); 344 SDL_InitSubSystem(SDL_INIT_VIDEO);
302 // SDL disables the screen saver by default, and setting the hint 345 // SDL disables the screen saver by default, and setting the hint
@@ -827,12 +870,11 @@ void GMainWindow::InitializeWidgets() {
827 870
828 // Setup Dock button 871 // Setup Dock button
829 dock_status_button = new QPushButton(); 872 dock_status_button = new QPushButton();
830 dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 873 dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton"));
831 dock_status_button->setFocusPolicy(Qt::NoFocus); 874 dock_status_button->setFocusPolicy(Qt::NoFocus);
832 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); 875 connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode);
833 dock_status_button->setText(tr("DOCK"));
834 dock_status_button->setCheckable(true); 876 dock_status_button->setCheckable(true);
835 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); 877 UpdateDockedButton();
836 statusBar()->insertPermanentWidget(0, dock_status_button); 878 statusBar()->insertPermanentWidget(0, dock_status_button);
837 879
838 gpu_accuracy_button = new QPushButton(); 880 gpu_accuracy_button = new QPushButton();
@@ -1567,7 +1609,7 @@ void GMainWindow::ShutdownGame() {
1567 emu_speed_label->setVisible(false); 1609 emu_speed_label->setVisible(false);
1568 game_fps_label->setVisible(false); 1610 game_fps_label->setVisible(false);
1569 emu_frametime_label->setVisible(false); 1611 emu_frametime_label->setVisible(false);
1570 renderer_status_button->setEnabled(true); 1612 renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan);
1571 1613
1572 game_path.clear(); 1614 game_path.clear();
1573 1615
@@ -1587,7 +1629,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) {
1587 1629
1588void GMainWindow::UpdateRecentFiles() { 1630void GMainWindow::UpdateRecentFiles() {
1589 const int num_recent_files = 1631 const int num_recent_files =
1590 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);
1591 1633
1592 for (int i = 0; i < num_recent_files; i++) { 1634 for (int i = 0; i < num_recent_files; i++) {
1593 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( 1635 const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg(
@@ -2777,6 +2819,10 @@ void GMainWindow::OnConfigure() {
2777 mouse_hide_timer.start(); 2819 mouse_hide_timer.start();
2778 } 2820 }
2779 2821
2822 if (!UISettings::values.has_broken_vulkan) {
2823 renderer_status_button->setEnabled(!emulation_running);
2824 }
2825
2780 UpdateStatusButtons(); 2826 UpdateStatusButtons();
2781 controller_dialog->refreshConfiguration(); 2827 controller_dialog->refreshConfiguration();
2782} 2828}
@@ -2862,7 +2908,7 @@ void GMainWindow::OnToggleDockedMode() {
2862 } 2908 }
2863 2909
2864 Settings::values.use_docked_mode.SetValue(!is_docked); 2910 Settings::values.use_docked_mode.SetValue(!is_docked);
2865 dock_status_button->setChecked(!is_docked); 2911 UpdateDockedButton();
2866 OnDockedModeChanged(is_docked, !is_docked, *system); 2912 OnDockedModeChanged(is_docked, !is_docked, *system);
2867} 2913}
2868 2914
@@ -3228,6 +3274,12 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3228 } 3274 }
3229} 3275}
3230 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
3231void GMainWindow::UpdateFilterText() { 3283void GMainWindow::UpdateFilterText() {
3232 const auto filter = Settings::values.scaling_filter.GetValue(); 3284 const auto filter = Settings::values.scaling_filter.GetValue();
3233 switch (filter) { 3285 switch (filter) {
@@ -3271,10 +3323,10 @@ void GMainWindow::UpdateAAText() {
3271} 3323}
3272 3324
3273void GMainWindow::UpdateStatusButtons() { 3325void GMainWindow::UpdateStatusButtons() {
3274 dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
3275 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3326 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3276 Settings::RendererBackend::Vulkan); 3327 Settings::RendererBackend::Vulkan);
3277 UpdateGPUAccuracyButton(); 3328 UpdateGPUAccuracyButton();
3329 UpdateDockedButton();
3278 UpdateFilterText(); 3330 UpdateFilterText();
3279 UpdateAAText(); 3331 UpdateAAText();
3280} 3332}
@@ -3325,7 +3377,7 @@ void GMainWindow::CenterMouseCursor() {
3325 const int center_x = render_window->width() / 2; 3377 const int center_x = render_window->width() / 2;
3326 const int center_y = render_window->height() / 2; 3378 const int center_y = render_window->height() / 2;
3327 3379
3328 QCursor::setPos(mapToGlobal({center_x, center_y})); 3380 QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
3329} 3381}
3330 3382
3331void 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();
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 15ba9ea17..c64d87ace 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -77,6 +77,8 @@ struct Values {
77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; 77 Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"};
78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; 78 Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"};
79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; 79 Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"};
80 // Set when Vulkan is known to crash the application
81 Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
80 82
81 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; 83 Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"};
82 84
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f34d6b728..39063e32b 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor =
218 218
219[Renderer] 219[Renderer]
220# Which backend API to use. 220# Which backend API to use.
221# 0 (default): OpenGL, 1: Vulkan 221# 0: OpenGL, 1 (default): Vulkan
222backend = 222backend =
223 223
224# Enable graphics API debugging mode. 224# Enable graphics API debugging mode.