summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--CMakeModules/FindLLVM.cmake16
-rw-r--r--LICENSES/LLVM-exception.txt15
-rw-r--r--externals/CMakeLists.txt10
-rw-r--r--externals/demangle/ItaniumDemangle.cpp588
-rw-r--r--externals/demangle/llvm/Demangle/Demangle.h104
-rw-r--r--externals/demangle/llvm/Demangle/DemangleConfig.h93
-rw-r--r--externals/demangle/llvm/Demangle/ItaniumDemangle.h5582
-rw-r--r--externals/demangle/llvm/Demangle/StringView.h127
-rw-r--r--externals/demangle/llvm/Demangle/Utility.h192
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/demangle.cpp35
-rw-r--r--src/common/demangle.h12
-rw-r--r--src/common/input.h68
-rw-r--r--src/common/settings.h1
-rw-r--r--src/core/arm/arm_interface.cpp22
-rw-r--r--src/core/hid/emulated_controller.cpp298
-rw-r--r--src/core/hid/emulated_controller.h64
-rw-r--r--src/core/hid/emulated_devices.cpp46
-rw-r--r--src/core/hid/emulated_devices.h18
-rw-r--r--src/core/hid/input_converter.cpp12
-rw-r--r--src/core/hid/input_converter.h10
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp12
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp2
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_page_table.cpp544
-rw-r--r--src/core/hle/kernel/k_page_table.h86
-rw-r--r--src/core/hle/kernel/k_process.cpp40
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/k_thread.cpp21
-rw-r--r--src/core/hle/kernel/k_thread.h24
-rw-r--r--src/core/hle/kernel/kernel.cpp39
-rw-r--r--src/core/hle/kernel/svc.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp18
-rw-r--r--src/core/hle/service/hid/hidbus.cpp24
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp8
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h4
-rw-r--r--src/core/hle/service/hid/irs.cpp18
-rw-r--r--src/core/hle/service/nfc/nfc_device.cpp7
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp7
-rw-r--r--src/core/memory.cpp2
-rw-r--r--src/input_common/CMakeLists.txt21
-rw-r--r--src/input_common/drivers/camera.cpp4
-rw-r--r--src/input_common/drivers/camera.h4
-rw-r--r--src/input_common/drivers/gc_adapter.cpp6
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/joycon.cpp678
-rw-r--r--src/input_common/drivers/joycon.h111
-rw-r--r--src/input_common/drivers/sdl_driver.cpp23
-rw-r--r--src/input_common/drivers/sdl_driver.h2
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp11
-rw-r--r--src/input_common/drivers/virtual_amiibo.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp575
-rw-r--r--src/input_common/helpers/joycon_driver.h150
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp184
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h64
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h173
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp136
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h114
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp298
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.h63
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h613
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp400
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h61
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp341
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.h81
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp117
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.h38
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.cpp299
-rw-r--r--src/input_common/helpers/joycon_protocol/rumble.h33
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp9
-rw-r--r--src/input_common/input_engine.cpp37
-rw-r--r--src/input_common/input_engine.h25
-rw-r--r--src/input_common/input_poller.cpp78
-rw-r--r--src/input_common/input_poller.h11
-rw-r--r--src/input_common/main.cpp14
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp5
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp8
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/tests/CMakeLists.txt3
-rw-r--r--src/tests/common/bit_field.cpp2
-rw-r--r--src/tests/common/cityhash.cpp2
-rw-r--r--src/tests/common/fibers.cpp2
-rw-r--r--src/tests/common/host_memory.cpp2
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/tests/common/range_map.cpp2
-rw-r--r--src/tests/common/ring_buffer.cpp2
-rw-r--r--src/tests/common/scratch_buffer.cpp2
-rw-r--r--src/tests/common/unique_function.cpp2
-rw-r--r--src/tests/core/core_timing.cpp2
-rw-r--r--src/tests/core/internal_network/network.cpp2
-rw-r--r--src/tests/input_common/calibration_configuration_job.cpp2
-rw-r--r--src/tests/tests.cpp8
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/memory_manager.cpp40
-rw-r--r--src/video_core/memory_manager.h3
-rw-r--r--src/video_core/vulkan_common/nsight_aftermath_tracker.cpp8
-rw-r--r--src/yuzu/configuration/config.cpp51
-rw-r--r--src/yuzu/configuration/configuration_shared.cpp10
-rw-r--r--src/yuzu/configuration/configuration_shared.h3
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp1
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp20
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp10
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp1
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp105
-rw-r--r--src/yuzu/configuration/configure_ringcon.h14
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui396
-rw-r--r--src/yuzu/configuration/configure_system.cpp10
-rw-r--r--src/yuzu/configuration/configure_system.h6
-rw-r--r--src/yuzu/configuration/configure_tas.cpp1
-rw-r--r--src/yuzu/debugger/controller.cpp5
-rw-r--r--src/yuzu/debugger/profiler.cpp5
-rw-r--r--src/yuzu/hotkeys.cpp6
-rw-r--r--src/yuzu/hotkeys.h1
-rw-r--r--src/yuzu/install_dialog.cpp1
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu/util/limitable_input_dialog.cpp2
-rw-r--r--src/yuzu/util/sequence_dialog/sequence_dialog.cpp1
-rw-r--r--src/yuzu_cmd/default_ini.h10
-rw-r--r--vcpkg.json2
127 files changed, 13273 insertions, 809 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cee720940..b3329318a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,12 +3,14 @@
3 3
4cmake_minimum_required(VERSION 3.22) 4cmake_minimum_required(VERSION 3.22)
5 5
6project(yuzu)
7
6list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules") 8list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
7list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules") 9list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
10
8include(DownloadExternals) 11include(DownloadExternals)
9include(CMakeDependentOption) 12include(CMakeDependentOption)
10 13include(CTest)
11project(yuzu)
12 14
13# Set bundled sdl2/qt as dependent options. 15# Set bundled sdl2/qt as dependent options.
14# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON 16# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
@@ -42,7 +44,7 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
42 44
43option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) 45option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
44 46
45option(YUZU_TESTS "Compile tests" ON) 47option(YUZU_TESTS "Compile tests" "${BUILD_TESTING}")
46 48
47option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON) 49option(YUZU_USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
48 50
@@ -206,6 +208,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
206find_package(enet 1.3 MODULE) 208find_package(enet 1.3 MODULE)
207find_package(fmt 9 REQUIRED) 209find_package(fmt 9 REQUIRED)
208find_package(inih MODULE) 210find_package(inih MODULE)
211find_package(LLVM MODULE)
209find_package(lz4 REQUIRED) 212find_package(lz4 REQUIRED)
210find_package(nlohmann_json 3.8 REQUIRED) 213find_package(nlohmann_json 3.8 REQUIRED)
211find_package(Opus 1.3 MODULE) 214find_package(Opus 1.3 MODULE)
@@ -242,7 +245,7 @@ if (ENABLE_WEB_SERVICE)
242endif() 245endif()
243 246
244if (YUZU_TESTS) 247if (YUZU_TESTS)
245 find_package(Catch2 2.13.7 REQUIRED) 248 find_package(Catch2 3.0.1 REQUIRED)
246endif() 249endif()
247 250
248find_package(Boost 1.73.0 COMPONENTS context) 251find_package(Boost 1.73.0 COMPONENTS context)
@@ -606,7 +609,6 @@ if (YUZU_USE_FASTER_LD AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
606 endif() 609 endif()
607endif() 610endif()
608 611
609enable_testing()
610add_subdirectory(externals) 612add_subdirectory(externals)
611add_subdirectory(src) 613add_subdirectory(src)
612 614
diff --git a/CMakeModules/FindLLVM.cmake b/CMakeModules/FindLLVM.cmake
new file mode 100644
index 000000000..513d9a536
--- /dev/null
+++ b/CMakeModules/FindLLVM.cmake
@@ -0,0 +1,16 @@
1# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4
5find_package(LLVM QUIET CONFIG)
6
7include(FindPackageHandleStandardArgs)
8find_package_handle_standard_args(LLVM CONFIG_MODE)
9
10if (LLVM_FOUND AND NOT TARGET LLVM::Demangle)
11 add_library(LLVM::Demangle INTERFACE IMPORTED)
12 llvm_map_components_to_libnames(LLVM_LIBRARIES demangle)
13 target_compile_definitions(LLVM::Demangle INTERFACE ${LLVM_DEFINITIONS})
14 target_include_directories(LLVM::Demangle INTERFACE ${LLVM_INCLUDE_DIRS})
15 target_link_libraries(LLVM::Demangle INTERFACE ${LLVM_LIBRARIES})
16endif()
diff --git a/LICENSES/LLVM-exception.txt b/LICENSES/LLVM-exception.txt
new file mode 100644
index 000000000..fa4b725a0
--- /dev/null
+++ b/LICENSES/LLVM-exception.txt
@@ -0,0 +1,15 @@
1---- LLVM Exceptions to the Apache 2.0 License ----
2
3 As an exception, if, as a result of your compiling your source code, portions
4 of this Software are embedded into an Object form of such source code, you
5 may redistribute such embedded portions in such Object form without complying
6 with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
7
8 In addition, if you combine or link compiled forms of this Software with
9 software that is licensed under the GPLv2 ("Combined Software") and if a
10 court of competent jurisdiction determines that the patent provision (Section
11 3), the indemnity provision (Section 9) or other Section of the License
12 conflicts with the conditions of the GPLv2, you may retroactively and
13 prospectively choose to deem waived or otherwise exclude such Section(s) of
14 the License, but only in their entirety and only with respect to the Combined
15 Software.
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index dfd40cba6..8532fd7a8 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -5,6 +5,9 @@
5# some of its variables, which is only possible in 3.13+ 5# some of its variables, which is only possible in 3.13+
6set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) 6set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
7 7
8# Disable tests in all externals supporting the standard option name
9set(BUILD_TESTING OFF)
10
8# xbyak 11# xbyak
9if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak) 12if ((ARCHITECTURE_x86 OR ARCHITECTURE_x86_64) AND NOT TARGET xbyak::xbyak)
10 add_subdirectory(xbyak EXCLUDE_FROM_ALL) 13 add_subdirectory(xbyak EXCLUDE_FROM_ALL)
@@ -154,3 +157,10 @@ endif()
154if (YUZU_USE_EXTERNAL_VULKAN_HEADERS) 157if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
155 add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL) 158 add_subdirectory(Vulkan-Headers EXCLUDE_FROM_ALL)
156endif() 159endif()
160
161if (NOT TARGET LLVM::Demangle)
162 add_library(demangle STATIC)
163 target_include_directories(demangle PUBLIC ./demangle)
164 target_sources(demangle PRIVATE demangle/ItaniumDemangle.cpp)
165 add_library(LLVM::Demangle ALIAS demangle)
166endif()
diff --git a/externals/demangle/ItaniumDemangle.cpp b/externals/demangle/ItaniumDemangle.cpp
new file mode 100644
index 000000000..b055a2fd7
--- /dev/null
+++ b/externals/demangle/ItaniumDemangle.cpp
@@ -0,0 +1,588 @@
1//===------------------------- ItaniumDemangle.cpp ------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10// FIXME: (possibly) incomplete list of features that clang mangles that this
11// file does not yet support:
12// - C++ modules TS
13
14#include "llvm/Demangle/Demangle.h"
15#include "llvm/Demangle/ItaniumDemangle.h"
16
17#include <cassert>
18#include <cctype>
19#include <cstdio>
20#include <cstdlib>
21#include <cstring>
22#include <functional>
23#include <numeric>
24#include <utility>
25#include <vector>
26
27using namespace llvm;
28using namespace llvm::itanium_demangle;
29
30constexpr const char *itanium_demangle::FloatData<float>::spec;
31constexpr const char *itanium_demangle::FloatData<double>::spec;
32constexpr const char *itanium_demangle::FloatData<long double>::spec;
33
34// <discriminator> := _ <non-negative number> # when number < 10
35// := __ <non-negative number> _ # when number >= 10
36// extension := decimal-digit+ # at the end of string
37const char *itanium_demangle::parse_discriminator(const char *first,
38 const char *last) {
39 // parse but ignore discriminator
40 if (first != last) {
41 if (*first == '_') {
42 const char *t1 = first + 1;
43 if (t1 != last) {
44 if (std::isdigit(*t1))
45 first = t1 + 1;
46 else if (*t1 == '_') {
47 for (++t1; t1 != last && std::isdigit(*t1); ++t1)
48 ;
49 if (t1 != last && *t1 == '_')
50 first = t1 + 1;
51 }
52 }
53 } else if (std::isdigit(*first)) {
54 const char *t1 = first + 1;
55 for (; t1 != last && std::isdigit(*t1); ++t1)
56 ;
57 if (t1 == last)
58 first = last;
59 }
60 }
61 return first;
62}
63
64#ifndef NDEBUG
65namespace {
66struct DumpVisitor {
67 unsigned Depth = 0;
68 bool PendingNewline = false;
69
70 template<typename NodeT> static constexpr bool wantsNewline(const NodeT *) {
71 return true;
72 }
73 static bool wantsNewline(NodeArray A) { return !A.empty(); }
74 static constexpr bool wantsNewline(...) { return false; }
75
76 template<typename ...Ts> static bool anyWantNewline(Ts ...Vs) {
77 for (bool B : {wantsNewline(Vs)...})
78 if (B)
79 return true;
80 return false;
81 }
82
83 void printStr(const char *S) { fprintf(stderr, "%s", S); }
84 void print(StringView SV) {
85 fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin());
86 }
87 void print(const Node *N) {
88 if (N)
89 N->visit(std::ref(*this));
90 else
91 printStr("<null>");
92 }
93 void print(NodeOrString NS) {
94 if (NS.isNode())
95 print(NS.asNode());
96 else if (NS.isString())
97 print(NS.asString());
98 else
99 printStr("NodeOrString()");
100 }
101 void print(NodeArray A) {
102 ++Depth;
103 printStr("{");
104 bool First = true;
105 for (const Node *N : A) {
106 if (First)
107 print(N);
108 else
109 printWithComma(N);
110 First = false;
111 }
112 printStr("}");
113 --Depth;
114 }
115
116 // Overload used when T is exactly 'bool', not merely convertible to 'bool'.
117 void print(bool B) { printStr(B ? "true" : "false"); }
118
119 template <class T>
120 typename std::enable_if<std::is_unsigned<T>::value>::type print(T N) {
121 fprintf(stderr, "%llu", (unsigned long long)N);
122 }
123
124 template <class T>
125 typename std::enable_if<std::is_signed<T>::value>::type print(T N) {
126 fprintf(stderr, "%lld", (long long)N);
127 }
128
129 void print(ReferenceKind RK) {
130 switch (RK) {
131 case ReferenceKind::LValue:
132 return printStr("ReferenceKind::LValue");
133 case ReferenceKind::RValue:
134 return printStr("ReferenceKind::RValue");
135 }
136 }
137 void print(FunctionRefQual RQ) {
138 switch (RQ) {
139 case FunctionRefQual::FrefQualNone:
140 return printStr("FunctionRefQual::FrefQualNone");
141 case FunctionRefQual::FrefQualLValue:
142 return printStr("FunctionRefQual::FrefQualLValue");
143 case FunctionRefQual::FrefQualRValue:
144 return printStr("FunctionRefQual::FrefQualRValue");
145 }
146 }
147 void print(Qualifiers Qs) {
148 if (!Qs) return printStr("QualNone");
149 struct QualName { Qualifiers Q; const char *Name; } Names[] = {
150 {QualConst, "QualConst"},
151 {QualVolatile, "QualVolatile"},
152 {QualRestrict, "QualRestrict"},
153 };
154 for (QualName Name : Names) {
155 if (Qs & Name.Q) {
156 printStr(Name.Name);
157 Qs = Qualifiers(Qs & ~Name.Q);
158 if (Qs) printStr(" | ");
159 }
160 }
161 }
162 void print(SpecialSubKind SSK) {
163 switch (SSK) {
164 case SpecialSubKind::allocator:
165 return printStr("SpecialSubKind::allocator");
166 case SpecialSubKind::basic_string:
167 return printStr("SpecialSubKind::basic_string");
168 case SpecialSubKind::string:
169 return printStr("SpecialSubKind::string");
170 case SpecialSubKind::istream:
171 return printStr("SpecialSubKind::istream");
172 case SpecialSubKind::ostream:
173 return printStr("SpecialSubKind::ostream");
174 case SpecialSubKind::iostream:
175 return printStr("SpecialSubKind::iostream");
176 }
177 }
178 void print(TemplateParamKind TPK) {
179 switch (TPK) {
180 case TemplateParamKind::Type:
181 return printStr("TemplateParamKind::Type");
182 case TemplateParamKind::NonType:
183 return printStr("TemplateParamKind::NonType");
184 case TemplateParamKind::Template:
185 return printStr("TemplateParamKind::Template");
186 }
187 }
188
189 void newLine() {
190 printStr("\n");
191 for (unsigned I = 0; I != Depth; ++I)
192 printStr(" ");
193 PendingNewline = false;
194 }
195
196 template<typename T> void printWithPendingNewline(T V) {
197 print(V);
198 if (wantsNewline(V))
199 PendingNewline = true;
200 }
201
202 template<typename T> void printWithComma(T V) {
203 if (PendingNewline || wantsNewline(V)) {
204 printStr(",");
205 newLine();
206 } else {
207 printStr(", ");
208 }
209
210 printWithPendingNewline(V);
211 }
212
213 struct CtorArgPrinter {
214 DumpVisitor &Visitor;
215
216 template<typename T, typename ...Rest> void operator()(T V, Rest ...Vs) {
217 if (Visitor.anyWantNewline(V, Vs...))
218 Visitor.newLine();
219 Visitor.printWithPendingNewline(V);
220 int PrintInOrder[] = { (Visitor.printWithComma(Vs), 0)..., 0 };
221 (void)PrintInOrder;
222 }
223 };
224
225 template<typename NodeT> void operator()(const NodeT *Node) {
226 Depth += 2;
227 fprintf(stderr, "%s(", itanium_demangle::NodeKind<NodeT>::name());
228 Node->match(CtorArgPrinter{*this});
229 fprintf(stderr, ")");
230 Depth -= 2;
231 }
232
233 void operator()(const ForwardTemplateReference *Node) {
234 Depth += 2;
235 fprintf(stderr, "ForwardTemplateReference(");
236 if (Node->Ref && !Node->Printing) {
237 Node->Printing = true;
238 CtorArgPrinter{*this}(Node->Ref);
239 Node->Printing = false;
240 } else {
241 CtorArgPrinter{*this}(Node->Index);
242 }
243 fprintf(stderr, ")");
244 Depth -= 2;
245 }
246};
247}
248
249void itanium_demangle::Node::dump() const {
250 DumpVisitor V;
251 visit(std::ref(V));
252 V.newLine();
253}
254#endif
255
256namespace {
257class BumpPointerAllocator {
258 struct BlockMeta {
259 BlockMeta* Next;
260 size_t Current;
261 };
262
263 static constexpr size_t AllocSize = 4096;
264 static constexpr size_t UsableAllocSize = AllocSize - sizeof(BlockMeta);
265
266 alignas(long double) char InitialBuffer[AllocSize];
267 BlockMeta* BlockList = nullptr;
268
269 void grow() {
270 char* NewMeta = static_cast<char *>(std::malloc(AllocSize));
271 if (NewMeta == nullptr)
272 std::terminate();
273 BlockList = new (NewMeta) BlockMeta{BlockList, 0};
274 }
275
276 void* allocateMassive(size_t NBytes) {
277 NBytes += sizeof(BlockMeta);
278 BlockMeta* NewMeta = reinterpret_cast<BlockMeta*>(std::malloc(NBytes));
279 if (NewMeta == nullptr)
280 std::terminate();
281 BlockList->Next = new (NewMeta) BlockMeta{BlockList->Next, 0};
282 return static_cast<void*>(NewMeta + 1);
283 }
284
285public:
286 BumpPointerAllocator()
287 : BlockList(new (InitialBuffer) BlockMeta{nullptr, 0}) {}
288
289 void* allocate(size_t N) {
290 N = (N + 15u) & ~15u;
291 if (N + BlockList->Current >= UsableAllocSize) {
292 if (N > UsableAllocSize)
293 return allocateMassive(N);
294 grow();
295 }
296 BlockList->Current += N;
297 return static_cast<void*>(reinterpret_cast<char*>(BlockList + 1) +
298 BlockList->Current - N);
299 }
300
301 void reset() {
302 while (BlockList) {
303 BlockMeta* Tmp = BlockList;
304 BlockList = BlockList->Next;
305 if (reinterpret_cast<char*>(Tmp) != InitialBuffer)
306 std::free(Tmp);
307 }
308 BlockList = new (InitialBuffer) BlockMeta{nullptr, 0};
309 }
310
311 ~BumpPointerAllocator() { reset(); }
312};
313
314class DefaultAllocator {
315 BumpPointerAllocator Alloc;
316
317public:
318 void reset() { Alloc.reset(); }
319
320 template<typename T, typename ...Args> T *makeNode(Args &&...args) {
321 return new (Alloc.allocate(sizeof(T)))
322 T(std::forward<Args>(args)...);
323 }
324
325 void *allocateNodeArray(size_t sz) {
326 return Alloc.allocate(sizeof(Node *) * sz);
327 }
328};
329} // unnamed namespace
330
331//===----------------------------------------------------------------------===//
332// Code beyond this point should not be synchronized with libc++abi.
333//===----------------------------------------------------------------------===//
334
335using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>;
336
337char *llvm::itaniumDemangle(const char *MangledName, char *Buf,
338 size_t *N, int *Status) {
339 if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) {
340 if (Status)
341 *Status = demangle_invalid_args;
342 return nullptr;
343 }
344
345 int InternalStatus = demangle_success;
346 Demangler Parser(MangledName, MangledName + std::strlen(MangledName));
347 OutputStream S;
348
349 Node *AST = Parser.parse();
350
351 if (AST == nullptr)
352 InternalStatus = demangle_invalid_mangled_name;
353 else if (!initializeOutputStream(Buf, N, S, 1024))
354 InternalStatus = demangle_memory_alloc_failure;
355 else {
356 assert(Parser.ForwardTemplateRefs.empty());
357 AST->print(S);
358 S += '\0';
359 if (N != nullptr)
360 *N = S.getCurrentPosition();
361 Buf = S.getBuffer();
362 }
363
364 if (Status)
365 *Status = InternalStatus;
366 return InternalStatus == demangle_success ? Buf : nullptr;
367}
368
369ItaniumPartialDemangler::ItaniumPartialDemangler()
370 : RootNode(nullptr), Context(new Demangler{nullptr, nullptr}) {}
371
372ItaniumPartialDemangler::~ItaniumPartialDemangler() {
373 delete static_cast<Demangler *>(Context);
374}
375
376ItaniumPartialDemangler::ItaniumPartialDemangler(
377 ItaniumPartialDemangler &&Other)
378 : RootNode(Other.RootNode), Context(Other.Context) {
379 Other.Context = Other.RootNode = nullptr;
380}
381
382ItaniumPartialDemangler &ItaniumPartialDemangler::
383operator=(ItaniumPartialDemangler &&Other) {
384 std::swap(RootNode, Other.RootNode);
385 std::swap(Context, Other.Context);
386 return *this;
387}
388
389// Demangle MangledName into an AST, storing it into this->RootNode.
390bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {
391 Demangler *Parser = static_cast<Demangler *>(Context);
392 size_t Len = std::strlen(MangledName);
393 Parser->reset(MangledName, MangledName + Len);
394 RootNode = Parser->parse();
395 return RootNode == nullptr;
396}
397
398static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
399 OutputStream S;
400 if (!initializeOutputStream(Buf, N, S, 128))
401 return nullptr;
402 RootNode->print(S);
403 S += '\0';
404 if (N != nullptr)
405 *N = S.getCurrentPosition();
406 return S.getBuffer();
407}
408
409char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {
410 if (!isFunction())
411 return nullptr;
412
413 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
414
415 while (true) {
416 switch (Name->getKind()) {
417 case Node::KAbiTagAttr:
418 Name = static_cast<const AbiTagAttr *>(Name)->Base;
419 continue;
420 case Node::KStdQualifiedName:
421 Name = static_cast<const StdQualifiedName *>(Name)->Child;
422 continue;
423 case Node::KNestedName:
424 Name = static_cast<const NestedName *>(Name)->Name;
425 continue;
426 case Node::KLocalName:
427 Name = static_cast<const LocalName *>(Name)->Entity;
428 continue;
429 case Node::KNameWithTemplateArgs:
430 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
431 continue;
432 default:
433 return printNode(Name, Buf, N);
434 }
435 }
436}
437
438char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,
439 size_t *N) const {
440 if (!isFunction())
441 return nullptr;
442 const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
443
444 OutputStream S;
445 if (!initializeOutputStream(Buf, N, S, 128))
446 return nullptr;
447
448 KeepGoingLocalFunction:
449 while (true) {
450 if (Name->getKind() == Node::KAbiTagAttr) {
451 Name = static_cast<const AbiTagAttr *>(Name)->Base;
452 continue;
453 }
454 if (Name->getKind() == Node::KNameWithTemplateArgs) {
455 Name = static_cast<const NameWithTemplateArgs *>(Name)->Name;
456 continue;
457 }
458 break;
459 }
460
461 switch (Name->getKind()) {
462 case Node::KStdQualifiedName:
463 S += "std";
464 break;
465 case Node::KNestedName:
466 static_cast<const NestedName *>(Name)->Qual->print(S);
467 break;
468 case Node::KLocalName: {
469 auto *LN = static_cast<const LocalName *>(Name);
470 LN->Encoding->print(S);
471 S += "::";
472 Name = LN->Entity;
473 goto KeepGoingLocalFunction;
474 }
475 default:
476 break;
477 }
478 S += '\0';
479 if (N != nullptr)
480 *N = S.getCurrentPosition();
481 return S.getBuffer();
482}
483
484char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {
485 if (!isFunction())
486 return nullptr;
487 auto *Name = static_cast<FunctionEncoding *>(RootNode)->getName();
488 return printNode(Name, Buf, N);
489}
490
491char *ItaniumPartialDemangler::getFunctionParameters(char *Buf,
492 size_t *N) const {
493 if (!isFunction())
494 return nullptr;
495 NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();
496
497 OutputStream S;
498 if (!initializeOutputStream(Buf, N, S, 128))
499 return nullptr;
500
501 S += '(';
502 Params.printWithComma(S);
503 S += ')';
504 S += '\0';
505 if (N != nullptr)
506 *N = S.getCurrentPosition();
507 return S.getBuffer();
508}
509
510char *ItaniumPartialDemangler::getFunctionReturnType(
511 char *Buf, size_t *N) const {
512 if (!isFunction())
513 return nullptr;
514
515 OutputStream S;
516 if (!initializeOutputStream(Buf, N, S, 128))
517 return nullptr;
518
519 if (const Node *Ret =
520 static_cast<const FunctionEncoding *>(RootNode)->getReturnType())
521 Ret->print(S);
522
523 S += '\0';
524 if (N != nullptr)
525 *N = S.getCurrentPosition();
526 return S.getBuffer();
527}
528
529char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {
530 assert(RootNode != nullptr && "must call partialDemangle()");
531 return printNode(static_cast<Node *>(RootNode), Buf, N);
532}
533
534bool ItaniumPartialDemangler::hasFunctionQualifiers() const {
535 assert(RootNode != nullptr && "must call partialDemangle()");
536 if (!isFunction())
537 return false;
538 auto *E = static_cast<const FunctionEncoding *>(RootNode);
539 return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;
540}
541
542bool ItaniumPartialDemangler::isCtorOrDtor() const {
543 const Node *N = static_cast<const Node *>(RootNode);
544 while (N) {
545 switch (N->getKind()) {
546 default:
547 return false;
548 case Node::KCtorDtorName:
549 return true;
550
551 case Node::KAbiTagAttr:
552 N = static_cast<const AbiTagAttr *>(N)->Base;
553 break;
554 case Node::KFunctionEncoding:
555 N = static_cast<const FunctionEncoding *>(N)->getName();
556 break;
557 case Node::KLocalName:
558 N = static_cast<const LocalName *>(N)->Entity;
559 break;
560 case Node::KNameWithTemplateArgs:
561 N = static_cast<const NameWithTemplateArgs *>(N)->Name;
562 break;
563 case Node::KNestedName:
564 N = static_cast<const NestedName *>(N)->Name;
565 break;
566 case Node::KStdQualifiedName:
567 N = static_cast<const StdQualifiedName *>(N)->Child;
568 break;
569 }
570 }
571 return false;
572}
573
574bool ItaniumPartialDemangler::isFunction() const {
575 assert(RootNode != nullptr && "must call partialDemangle()");
576 return static_cast<const Node *>(RootNode)->getKind() ==
577 Node::KFunctionEncoding;
578}
579
580bool ItaniumPartialDemangler::isSpecialName() const {
581 assert(RootNode != nullptr && "must call partialDemangle()");
582 auto K = static_cast<const Node *>(RootNode)->getKind();
583 return K == Node::KSpecialName || K == Node::KCtorVtableSpecialName;
584}
585
586bool ItaniumPartialDemangler::isData() const {
587 return !isFunction() && !isSpecialName();
588}
diff --git a/externals/demangle/llvm/Demangle/Demangle.h b/externals/demangle/llvm/Demangle/Demangle.h
new file mode 100644
index 000000000..5b673e4e1
--- /dev/null
+++ b/externals/demangle/llvm/Demangle/Demangle.h
@@ -0,0 +1,104 @@
1//===--- Demangle.h ---------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLVM_DEMANGLE_DEMANGLE_H
11#define LLVM_DEMANGLE_DEMANGLE_H
12
13#include <cstddef>
14#include <string>
15
16namespace llvm {
17/// This is a llvm local version of __cxa_demangle. Other than the name and
18/// being in the llvm namespace it is identical.
19///
20/// The mangled_name is demangled into buf and returned. If the buffer is not
21/// large enough, realloc is used to expand it.
22///
23/// The *status will be set to a value from the following enumeration
24enum : int {
25 demangle_unknown_error = -4,
26 demangle_invalid_args = -3,
27 demangle_invalid_mangled_name = -2,
28 demangle_memory_alloc_failure = -1,
29 demangle_success = 0,
30};
31
32char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n,
33 int *status);
34
35
36enum MSDemangleFlags {
37 MSDF_None = 0,
38 MSDF_DumpBackrefs = 1 << 0,
39 MSDF_NoAccessSpecifier = 1 << 1,
40 MSDF_NoCallingConvention = 1 << 2,
41 MSDF_NoReturnType = 1 << 3,
42 MSDF_NoMemberType = 1 << 4,
43};
44char *microsoftDemangle(const char *mangled_name, char *buf, size_t *n,
45 int *status, MSDemangleFlags Flags = MSDF_None);
46
47/// "Partial" demangler. This supports demangling a string into an AST
48/// (typically an intermediate stage in itaniumDemangle) and querying certain
49/// properties or partially printing the demangled name.
50struct ItaniumPartialDemangler {
51 ItaniumPartialDemangler();
52
53 ItaniumPartialDemangler(ItaniumPartialDemangler &&Other);
54 ItaniumPartialDemangler &operator=(ItaniumPartialDemangler &&Other);
55
56 /// Demangle into an AST. Subsequent calls to the rest of the member functions
57 /// implicitly operate on the AST this produces.
58 /// \return true on error, false otherwise
59 bool partialDemangle(const char *MangledName);
60
61 /// Just print the entire mangled name into Buf. Buf and N behave like the
62 /// second and third parameters to itaniumDemangle.
63 char *finishDemangle(char *Buf, size_t *N) const;
64
65 /// Get the base name of a function. This doesn't include trailing template
66 /// arguments, ie for "a::b<int>" this function returns "b".
67 char *getFunctionBaseName(char *Buf, size_t *N) const;
68
69 /// Get the context name for a function. For "a::b::c", this function returns
70 /// "a::b".
71 char *getFunctionDeclContextName(char *Buf, size_t *N) const;
72
73 /// Get the entire name of this function.
74 char *getFunctionName(char *Buf, size_t *N) const;
75
76 /// Get the parameters for this function.
77 char *getFunctionParameters(char *Buf, size_t *N) const;
78 char *getFunctionReturnType(char *Buf, size_t *N) const;
79
80 /// If this function has any any cv or reference qualifiers. These imply that
81 /// the function is a non-static member function.
82 bool hasFunctionQualifiers() const;
83
84 /// If this symbol describes a constructor or destructor.
85 bool isCtorOrDtor() const;
86
87 /// If this symbol describes a function.
88 bool isFunction() const;
89
90 /// If this symbol describes a variable.
91 bool isData() const;
92
93 /// If this symbol is a <special-name>. These are generally implicitly
94 /// generated by the implementation, such as vtables and typeinfo names.
95 bool isSpecialName() const;
96
97 ~ItaniumPartialDemangler();
98private:
99 void *RootNode;
100 void *Context;
101};
102} // namespace llvm
103
104#endif
diff --git a/externals/demangle/llvm/Demangle/DemangleConfig.h b/externals/demangle/llvm/Demangle/DemangleConfig.h
new file mode 100644
index 000000000..a8aef9df1
--- /dev/null
+++ b/externals/demangle/llvm/Demangle/DemangleConfig.h
@@ -0,0 +1,93 @@
1//===--- DemangleConfig.h ---------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This file contains a variety of feature test macros copied from
11// include/llvm/Support/Compiler.h so that LLVMDemangle does not need to take
12// a dependency on LLVMSupport.
13//
14//===----------------------------------------------------------------------===//
15
16#ifndef LLVM_DEMANGLE_COMPILER_H
17#define LLVM_DEMANGLE_COMPILER_H
18
19#ifndef __has_feature
20#define __has_feature(x) 0
21#endif
22
23#ifndef __has_cpp_attribute
24#define __has_cpp_attribute(x) 0
25#endif
26
27#ifndef __has_attribute
28#define __has_attribute(x) 0
29#endif
30
31#ifndef __has_builtin
32#define __has_builtin(x) 0
33#endif
34
35#ifndef DEMANGLE_GNUC_PREREQ
36#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
37#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \
38 ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) + __GNUC_PATCHLEVEL__ >= \
39 ((maj) << 20) + ((min) << 10) + (patch))
40#elif defined(__GNUC__) && defined(__GNUC_MINOR__)
41#define DEMANGLE_GNUC_PREREQ(maj, min, patch) \
42 ((__GNUC__ << 20) + (__GNUC_MINOR__ << 10) >= ((maj) << 20) + ((min) << 10))
43#else
44#define DEMANGLE_GNUC_PREREQ(maj, min, patch) 0
45#endif
46#endif
47
48#if __has_attribute(used) || DEMANGLE_GNUC_PREREQ(3, 1, 0)
49#define DEMANGLE_ATTRIBUTE_USED __attribute__((__used__))
50#else
51#define DEMANGLE_ATTRIBUTE_USED
52#endif
53
54#if __has_builtin(__builtin_unreachable) || DEMANGLE_GNUC_PREREQ(4, 5, 0)
55#define DEMANGLE_UNREACHABLE __builtin_unreachable()
56#elif defined(_MSC_VER)
57#define DEMANGLE_UNREACHABLE __assume(false)
58#else
59#define DEMANGLE_UNREACHABLE
60#endif
61
62#if __has_attribute(noinline) || DEMANGLE_GNUC_PREREQ(3, 4, 0)
63#define DEMANGLE_ATTRIBUTE_NOINLINE __attribute__((noinline))
64#elif defined(_MSC_VER)
65#define DEMANGLE_ATTRIBUTE_NOINLINE __declspec(noinline)
66#else
67#define DEMANGLE_ATTRIBUTE_NOINLINE
68#endif
69
70#if !defined(NDEBUG)
71#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE DEMANGLE_ATTRIBUTE_USED
72#else
73#define DEMANGLE_DUMP_METHOD DEMANGLE_ATTRIBUTE_NOINLINE
74#endif
75
76#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough)
77#define DEMANGLE_FALLTHROUGH [[fallthrough]]
78#elif __has_cpp_attribute(gnu::fallthrough)
79#define DEMANGLE_FALLTHROUGH [[gnu::fallthrough]]
80#elif !__cplusplus
81// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious
82// error when __has_cpp_attribute is given a scoped attribute in C mode.
83#define DEMANGLE_FALLTHROUGH
84#elif __has_cpp_attribute(clang::fallthrough)
85#define DEMANGLE_FALLTHROUGH [[clang::fallthrough]]
86#else
87#define DEMANGLE_FALLTHROUGH
88#endif
89
90#define DEMANGLE_NAMESPACE_BEGIN namespace llvm { namespace itanium_demangle {
91#define DEMANGLE_NAMESPACE_END } }
92
93#endif
diff --git a/externals/demangle/llvm/Demangle/ItaniumDemangle.h b/externals/demangle/llvm/Demangle/ItaniumDemangle.h
new file mode 100644
index 000000000..64b35c142
--- /dev/null
+++ b/externals/demangle/llvm/Demangle/ItaniumDemangle.h
@@ -0,0 +1,5582 @@
1//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// Generic itanium demangler library. This file has two byte-per-byte identical
11// copies in the source tree, one in libcxxabi, and the other in llvm.
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef DEMANGLE_ITANIUMDEMANGLE_H
16#define DEMANGLE_ITANIUMDEMANGLE_H
17
18// FIXME: (possibly) incomplete list of features that clang mangles that this
19// file does not yet support:
20// - C++ modules TS
21
22#include "DemangleConfig.h"
23#include "StringView.h"
24#include "Utility.h"
25#include <cassert>
26#include <cctype>
27#include <cstdio>
28#include <cstdlib>
29#include <cstring>
30#include <numeric>
31#include <utility>
32
33#define FOR_EACH_NODE_KIND(X) \
34 X(NodeArrayNode) \
35 X(DotSuffix) \
36 X(VendorExtQualType) \
37 X(QualType) \
38 X(ConversionOperatorType) \
39 X(PostfixQualifiedType) \
40 X(ElaboratedTypeSpefType) \
41 X(NameType) \
42 X(AbiTagAttr) \
43 X(EnableIfAttr) \
44 X(ObjCProtoName) \
45 X(PointerType) \
46 X(ReferenceType) \
47 X(PointerToMemberType) \
48 X(ArrayType) \
49 X(FunctionType) \
50 X(NoexceptSpec) \
51 X(DynamicExceptionSpec) \
52 X(FunctionEncoding) \
53 X(LiteralOperator) \
54 X(SpecialName) \
55 X(CtorVtableSpecialName) \
56 X(QualifiedName) \
57 X(NestedName) \
58 X(LocalName) \
59 X(VectorType) \
60 X(PixelVectorType) \
61 X(SyntheticTemplateParamName) \
62 X(TypeTemplateParamDecl) \
63 X(NonTypeTemplateParamDecl) \
64 X(TemplateTemplateParamDecl) \
65 X(TemplateParamPackDecl) \
66 X(ParameterPack) \
67 X(TemplateArgumentPack) \
68 X(ParameterPackExpansion) \
69 X(TemplateArgs) \
70 X(ForwardTemplateReference) \
71 X(NameWithTemplateArgs) \
72 X(GlobalQualifiedName) \
73 X(StdQualifiedName) \
74 X(ExpandedSpecialSubstitution) \
75 X(SpecialSubstitution) \
76 X(CtorDtorName) \
77 X(DtorName) \
78 X(UnnamedTypeName) \
79 X(ClosureTypeName) \
80 X(StructuredBindingName) \
81 X(BinaryExpr) \
82 X(ArraySubscriptExpr) \
83 X(PostfixExpr) \
84 X(ConditionalExpr) \
85 X(MemberExpr) \
86 X(EnclosingExpr) \
87 X(CastExpr) \
88 X(SizeofParamPackExpr) \
89 X(CallExpr) \
90 X(NewExpr) \
91 X(DeleteExpr) \
92 X(PrefixExpr) \
93 X(FunctionParam) \
94 X(ConversionExpr) \
95 X(InitListExpr) \
96 X(FoldExpr) \
97 X(ThrowExpr) \
98 X(UUIDOfExpr) \
99 X(BoolExpr) \
100 X(StringLiteral) \
101 X(LambdaExpr) \
102 X(IntegerCastExpr) \
103 X(IntegerLiteral) \
104 X(FloatLiteral) \
105 X(DoubleLiteral) \
106 X(LongDoubleLiteral) \
107 X(BracedExpr) \
108 X(BracedRangeExpr)
109
110DEMANGLE_NAMESPACE_BEGIN
111
112// Base class of all AST nodes. The AST is built by the parser, then is
113// traversed by the printLeft/Right functions to produce a demangled string.
114class Node {
115public:
116 enum Kind : unsigned char {
117#define ENUMERATOR(NodeKind) K ## NodeKind,
118 FOR_EACH_NODE_KIND(ENUMERATOR)
119#undef ENUMERATOR
120 };
121
122 /// Three-way bool to track a cached value. Unknown is possible if this node
123 /// has an unexpanded parameter pack below it that may affect this cache.
124 enum class Cache : unsigned char { Yes, No, Unknown, };
125
126private:
127 Kind K;
128
129 // FIXME: Make these protected.
130public:
131 /// Tracks if this node has a component on its right side, in which case we
132 /// need to call printRight.
133 Cache RHSComponentCache;
134
135 /// Track if this node is a (possibly qualified) array type. This can affect
136 /// how we format the output string.
137 Cache ArrayCache;
138
139 /// Track if this node is a (possibly qualified) function type. This can
140 /// affect how we format the output string.
141 Cache FunctionCache;
142
143public:
144 Node(Kind K_, Cache RHSComponentCache_ = Cache::No,
145 Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No)
146 : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_),
147 FunctionCache(FunctionCache_) {}
148
149 /// Visit the most-derived object corresponding to this object.
150 template<typename Fn> void visit(Fn F) const;
151
152 // The following function is provided by all derived classes:
153 //
154 // Call F with arguments that, when passed to the constructor of this node,
155 // would construct an equivalent node.
156 //template<typename Fn> void match(Fn F) const;
157
158 bool hasRHSComponent(OutputStream &S) const {
159 if (RHSComponentCache != Cache::Unknown)
160 return RHSComponentCache == Cache::Yes;
161 return hasRHSComponentSlow(S);
162 }
163
164 bool hasArray(OutputStream &S) const {
165 if (ArrayCache != Cache::Unknown)
166 return ArrayCache == Cache::Yes;
167 return hasArraySlow(S);
168 }
169
170 bool hasFunction(OutputStream &S) const {
171 if (FunctionCache != Cache::Unknown)
172 return FunctionCache == Cache::Yes;
173 return hasFunctionSlow(S);
174 }
175
176 Kind getKind() const { return K; }
177
178 virtual bool hasRHSComponentSlow(OutputStream &) const { return false; }
179 virtual bool hasArraySlow(OutputStream &) const { return false; }
180 virtual bool hasFunctionSlow(OutputStream &) const { return false; }
181
182 // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to
183 // get at a node that actually represents some concrete syntax.
184 virtual const Node *getSyntaxNode(OutputStream &) const {
185 return this;
186 }
187
188 void print(OutputStream &S) const {
189 printLeft(S);
190 if (RHSComponentCache != Cache::No)
191 printRight(S);
192 }
193
194 // Print the "left" side of this Node into OutputStream.
195 virtual void printLeft(OutputStream &) const = 0;
196
197 // Print the "right". This distinction is necessary to represent C++ types
198 // that appear on the RHS of their subtype, such as arrays or functions.
199 // Since most types don't have such a component, provide a default
200 // implementation.
201 virtual void printRight(OutputStream &) const {}
202
203 virtual StringView getBaseName() const { return StringView(); }
204
205 // Silence compiler warnings, this dtor will never be called.
206 virtual ~Node() = default;
207
208#ifndef NDEBUG
209 DEMANGLE_DUMP_METHOD void dump() const;
210#endif
211};
212
213class NodeArray {
214 Node **Elements;
215 size_t NumElements;
216
217public:
218 NodeArray() : Elements(nullptr), NumElements(0) {}
219 NodeArray(Node **Elements_, size_t NumElements_)
220 : Elements(Elements_), NumElements(NumElements_) {}
221
222 bool empty() const { return NumElements == 0; }
223 size_t size() const { return NumElements; }
224
225 Node **begin() const { return Elements; }
226 Node **end() const { return Elements + NumElements; }
227
228 Node *operator[](size_t Idx) const { return Elements[Idx]; }
229
230 void printWithComma(OutputStream &S) const {
231 bool FirstElement = true;
232 for (size_t Idx = 0; Idx != NumElements; ++Idx) {
233 size_t BeforeComma = S.getCurrentPosition();
234 if (!FirstElement)
235 S += ", ";
236 size_t AfterComma = S.getCurrentPosition();
237 Elements[Idx]->print(S);
238
239 // Elements[Idx] is an empty parameter pack expansion, we should erase the
240 // comma we just printed.
241 if (AfterComma == S.getCurrentPosition()) {
242 S.setCurrentPosition(BeforeComma);
243 continue;
244 }
245
246 FirstElement = false;
247 }
248 }
249};
250
251struct NodeArrayNode : Node {
252 NodeArray Array;
253 NodeArrayNode(NodeArray Array_) : Node(KNodeArrayNode), Array(Array_) {}
254
255 template<typename Fn> void match(Fn F) const { F(Array); }
256
257 void printLeft(OutputStream &S) const override {
258 Array.printWithComma(S);
259 }
260};
261
262class DotSuffix final : public Node {
263 const Node *Prefix;
264 const StringView Suffix;
265
266public:
267 DotSuffix(const Node *Prefix_, StringView Suffix_)
268 : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {}
269
270 template<typename Fn> void match(Fn F) const { F(Prefix, Suffix); }
271
272 void printLeft(OutputStream &s) const override {
273 Prefix->print(s);
274 s += " (";
275 s += Suffix;
276 s += ")";
277 }
278};
279
280class VendorExtQualType final : public Node {
281 const Node *Ty;
282 StringView Ext;
283
284public:
285 VendorExtQualType(const Node *Ty_, StringView Ext_)
286 : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_) {}
287
288 template<typename Fn> void match(Fn F) const { F(Ty, Ext); }
289
290 void printLeft(OutputStream &S) const override {
291 Ty->print(S);
292 S += " ";
293 S += Ext;
294 }
295};
296
297enum FunctionRefQual : unsigned char {
298 FrefQualNone,
299 FrefQualLValue,
300 FrefQualRValue,
301};
302
303enum Qualifiers {
304 QualNone = 0,
305 QualConst = 0x1,
306 QualVolatile = 0x2,
307 QualRestrict = 0x4,
308};
309
310inline Qualifiers operator|=(Qualifiers &Q1, Qualifiers Q2) {
311 return Q1 = static_cast<Qualifiers>(Q1 | Q2);
312}
313
314class QualType final : public Node {
315protected:
316 const Qualifiers Quals;
317 const Node *Child;
318
319 void printQuals(OutputStream &S) const {
320 if (Quals & QualConst)
321 S += " const";
322 if (Quals & QualVolatile)
323 S += " volatile";
324 if (Quals & QualRestrict)
325 S += " restrict";
326 }
327
328public:
329 QualType(const Node *Child_, Qualifiers Quals_)
330 : Node(KQualType, Child_->RHSComponentCache,
331 Child_->ArrayCache, Child_->FunctionCache),
332 Quals(Quals_), Child(Child_) {}
333
334 template<typename Fn> void match(Fn F) const { F(Child, Quals); }
335
336 bool hasRHSComponentSlow(OutputStream &S) const override {
337 return Child->hasRHSComponent(S);
338 }
339 bool hasArraySlow(OutputStream &S) const override {
340 return Child->hasArray(S);
341 }
342 bool hasFunctionSlow(OutputStream &S) const override {
343 return Child->hasFunction(S);
344 }
345
346 void printLeft(OutputStream &S) const override {
347 Child->printLeft(S);
348 printQuals(S);
349 }
350
351 void printRight(OutputStream &S) const override { Child->printRight(S); }
352};
353
354class ConversionOperatorType final : public Node {
355 const Node *Ty;
356
357public:
358 ConversionOperatorType(const Node *Ty_)
359 : Node(KConversionOperatorType), Ty(Ty_) {}
360
361 template<typename Fn> void match(Fn F) const { F(Ty); }
362
363 void printLeft(OutputStream &S) const override {
364 S += "operator ";
365 Ty->print(S);
366 }
367};
368
369class PostfixQualifiedType final : public Node {
370 const Node *Ty;
371 const StringView Postfix;
372
373public:
374 PostfixQualifiedType(Node *Ty_, StringView Postfix_)
375 : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {}
376
377 template<typename Fn> void match(Fn F) const { F(Ty, Postfix); }
378
379 void printLeft(OutputStream &s) const override {
380 Ty->printLeft(s);
381 s += Postfix;
382 }
383};
384
385class NameType final : public Node {
386 const StringView Name;
387
388public:
389 NameType(StringView Name_) : Node(KNameType), Name(Name_) {}
390
391 template<typename Fn> void match(Fn F) const { F(Name); }
392
393 StringView getName() const { return Name; }
394 StringView getBaseName() const override { return Name; }
395
396 void printLeft(OutputStream &s) const override { s += Name; }
397};
398
399class ElaboratedTypeSpefType : public Node {
400 StringView Kind;
401 Node *Child;
402public:
403 ElaboratedTypeSpefType(StringView Kind_, Node *Child_)
404 : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {}
405
406 template<typename Fn> void match(Fn F) const { F(Kind, Child); }
407
408 void printLeft(OutputStream &S) const override {
409 S += Kind;
410 S += ' ';
411 Child->print(S);
412 }
413};
414
415struct AbiTagAttr : Node {
416 Node *Base;
417 StringView Tag;
418
419 AbiTagAttr(Node* Base_, StringView Tag_)
420 : Node(KAbiTagAttr, Base_->RHSComponentCache,
421 Base_->ArrayCache, Base_->FunctionCache),
422 Base(Base_), Tag(Tag_) {}
423
424 template<typename Fn> void match(Fn F) const { F(Base, Tag); }
425
426 void printLeft(OutputStream &S) const override {
427 Base->printLeft(S);
428 S += "[abi:";
429 S += Tag;
430 S += "]";
431 }
432};
433
434class EnableIfAttr : public Node {
435 NodeArray Conditions;
436public:
437 EnableIfAttr(NodeArray Conditions_)
438 : Node(KEnableIfAttr), Conditions(Conditions_) {}
439
440 template<typename Fn> void match(Fn F) const { F(Conditions); }
441
442 void printLeft(OutputStream &S) const override {
443 S += " [enable_if:";
444 Conditions.printWithComma(S);
445 S += ']';
446 }
447};
448
449class ObjCProtoName : public Node {
450 const Node *Ty;
451 StringView Protocol;
452
453 friend class PointerType;
454
455public:
456 ObjCProtoName(const Node *Ty_, StringView Protocol_)
457 : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {}
458
459 template<typename Fn> void match(Fn F) const { F(Ty, Protocol); }
460
461 bool isObjCObject() const {
462 return Ty->getKind() == KNameType &&
463 static_cast<const NameType *>(Ty)->getName() == "objc_object";
464 }
465
466 void printLeft(OutputStream &S) const override {
467 Ty->print(S);
468 S += "<";
469 S += Protocol;
470 S += ">";
471 }
472};
473
474class PointerType final : public Node {
475 const Node *Pointee;
476
477public:
478 PointerType(const Node *Pointee_)
479 : Node(KPointerType, Pointee_->RHSComponentCache),
480 Pointee(Pointee_) {}
481
482 template<typename Fn> void match(Fn F) const { F(Pointee); }
483
484 bool hasRHSComponentSlow(OutputStream &S) const override {
485 return Pointee->hasRHSComponent(S);
486 }
487
488 void printLeft(OutputStream &s) const override {
489 // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>.
490 if (Pointee->getKind() != KObjCProtoName ||
491 !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
492 Pointee->printLeft(s);
493 if (Pointee->hasArray(s))
494 s += " ";
495 if (Pointee->hasArray(s) || Pointee->hasFunction(s))
496 s += "(";
497 s += "*";
498 } else {
499 const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee);
500 s += "id<";
501 s += objcProto->Protocol;
502 s += ">";
503 }
504 }
505
506 void printRight(OutputStream &s) const override {
507 if (Pointee->getKind() != KObjCProtoName ||
508 !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) {
509 if (Pointee->hasArray(s) || Pointee->hasFunction(s))
510 s += ")";
511 Pointee->printRight(s);
512 }
513 }
514};
515
516enum class ReferenceKind {
517 LValue,
518 RValue,
519};
520
521// Represents either a LValue or an RValue reference type.
522class ReferenceType : public Node {
523 const Node *Pointee;
524 ReferenceKind RK;
525
526 mutable bool Printing = false;
527
528 // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The
529 // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any
530 // other combination collapses to a lvalue ref.
531 std::pair<ReferenceKind, const Node *> collapse(OutputStream &S) const {
532 auto SoFar = std::make_pair(RK, Pointee);
533 for (;;) {
534 const Node *SN = SoFar.second->getSyntaxNode(S);
535 if (SN->getKind() != KReferenceType)
536 break;
537 auto *RT = static_cast<const ReferenceType *>(SN);
538 SoFar.second = RT->Pointee;
539 SoFar.first = std::min(SoFar.first, RT->RK);
540 }
541 return SoFar;
542 }
543
544public:
545 ReferenceType(const Node *Pointee_, ReferenceKind RK_)
546 : Node(KReferenceType, Pointee_->RHSComponentCache),
547 Pointee(Pointee_), RK(RK_) {}
548
549 template<typename Fn> void match(Fn F) const { F(Pointee, RK); }
550
551 bool hasRHSComponentSlow(OutputStream &S) const override {
552 return Pointee->hasRHSComponent(S);
553 }
554
555 void printLeft(OutputStream &s) const override {
556 if (Printing)
557 return;
558 SwapAndRestore<bool> SavePrinting(Printing, true);
559 std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
560 Collapsed.second->printLeft(s);
561 if (Collapsed.second->hasArray(s))
562 s += " ";
563 if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s))
564 s += "(";
565
566 s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&");
567 }
568 void printRight(OutputStream &s) const override {
569 if (Printing)
570 return;
571 SwapAndRestore<bool> SavePrinting(Printing, true);
572 std::pair<ReferenceKind, const Node *> Collapsed = collapse(s);
573 if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s))
574 s += ")";
575 Collapsed.second->printRight(s);
576 }
577};
578
579class PointerToMemberType final : public Node {
580 const Node *ClassType;
581 const Node *MemberType;
582
583public:
584 PointerToMemberType(const Node *ClassType_, const Node *MemberType_)
585 : Node(KPointerToMemberType, MemberType_->RHSComponentCache),
586 ClassType(ClassType_), MemberType(MemberType_) {}
587
588 template<typename Fn> void match(Fn F) const { F(ClassType, MemberType); }
589
590 bool hasRHSComponentSlow(OutputStream &S) const override {
591 return MemberType->hasRHSComponent(S);
592 }
593
594 void printLeft(OutputStream &s) const override {
595 MemberType->printLeft(s);
596 if (MemberType->hasArray(s) || MemberType->hasFunction(s))
597 s += "(";
598 else
599 s += " ";
600 ClassType->print(s);
601 s += "::*";
602 }
603
604 void printRight(OutputStream &s) const override {
605 if (MemberType->hasArray(s) || MemberType->hasFunction(s))
606 s += ")";
607 MemberType->printRight(s);
608 }
609};
610
611class NodeOrString {
612 const void *First;
613 const void *Second;
614
615public:
616 /* implicit */ NodeOrString(StringView Str) {
617 const char *FirstChar = Str.begin();
618 const char *SecondChar = Str.end();
619 if (SecondChar == nullptr) {
620 assert(FirstChar == SecondChar);
621 ++FirstChar, ++SecondChar;
622 }
623 First = static_cast<const void *>(FirstChar);
624 Second = static_cast<const void *>(SecondChar);
625 }
626
627 /* implicit */ NodeOrString(Node *N)
628 : First(static_cast<const void *>(N)), Second(nullptr) {}
629 NodeOrString() : First(nullptr), Second(nullptr) {}
630
631 bool isString() const { return Second && First; }
632 bool isNode() const { return First && !Second; }
633 bool isEmpty() const { return !First && !Second; }
634
635 StringView asString() const {
636 assert(isString());
637 return StringView(static_cast<const char *>(First),
638 static_cast<const char *>(Second));
639 }
640
641 const Node *asNode() const {
642 assert(isNode());
643 return static_cast<const Node *>(First);
644 }
645};
646
647class ArrayType final : public Node {
648 const Node *Base;
649 NodeOrString Dimension;
650
651public:
652 ArrayType(const Node *Base_, NodeOrString Dimension_)
653 : Node(KArrayType,
654 /*RHSComponentCache=*/Cache::Yes,
655 /*ArrayCache=*/Cache::Yes),
656 Base(Base_), Dimension(Dimension_) {}
657
658 template<typename Fn> void match(Fn F) const { F(Base, Dimension); }
659
660 bool hasRHSComponentSlow(OutputStream &) const override { return true; }
661 bool hasArraySlow(OutputStream &) const override { return true; }
662
663 void printLeft(OutputStream &S) const override { Base->printLeft(S); }
664
665 void printRight(OutputStream &S) const override {
666 if (S.back() != ']')
667 S += " ";
668 S += "[";
669 if (Dimension.isString())
670 S += Dimension.asString();
671 else if (Dimension.isNode())
672 Dimension.asNode()->print(S);
673 S += "]";
674 Base->printRight(S);
675 }
676};
677
678class FunctionType final : public Node {
679 const Node *Ret;
680 NodeArray Params;
681 Qualifiers CVQuals;
682 FunctionRefQual RefQual;
683 const Node *ExceptionSpec;
684
685public:
686 FunctionType(const Node *Ret_, NodeArray Params_, Qualifiers CVQuals_,
687 FunctionRefQual RefQual_, const Node *ExceptionSpec_)
688 : Node(KFunctionType,
689 /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
690 /*FunctionCache=*/Cache::Yes),
691 Ret(Ret_), Params(Params_), CVQuals(CVQuals_), RefQual(RefQual_),
692 ExceptionSpec(ExceptionSpec_) {}
693
694 template<typename Fn> void match(Fn F) const {
695 F(Ret, Params, CVQuals, RefQual, ExceptionSpec);
696 }
697
698 bool hasRHSComponentSlow(OutputStream &) const override { return true; }
699 bool hasFunctionSlow(OutputStream &) const override { return true; }
700
701 // Handle C++'s ... quirky decl grammar by using the left & right
702 // distinction. Consider:
703 // int (*f(float))(char) {}
704 // f is a function that takes a float and returns a pointer to a function
705 // that takes a char and returns an int. If we're trying to print f, start
706 // by printing out the return types's left, then print our parameters, then
707 // finally print right of the return type.
708 void printLeft(OutputStream &S) const override {
709 Ret->printLeft(S);
710 S += " ";
711 }
712
713 void printRight(OutputStream &S) const override {
714 S += "(";
715 Params.printWithComma(S);
716 S += ")";
717 Ret->printRight(S);
718
719 if (CVQuals & QualConst)
720 S += " const";
721 if (CVQuals & QualVolatile)
722 S += " volatile";
723 if (CVQuals & QualRestrict)
724 S += " restrict";
725
726 if (RefQual == FrefQualLValue)
727 S += " &";
728 else if (RefQual == FrefQualRValue)
729 S += " &&";
730
731 if (ExceptionSpec != nullptr) {
732 S += ' ';
733 ExceptionSpec->print(S);
734 }
735 }
736};
737
738class NoexceptSpec : public Node {
739 const Node *E;
740public:
741 NoexceptSpec(const Node *E_) : Node(KNoexceptSpec), E(E_) {}
742
743 template<typename Fn> void match(Fn F) const { F(E); }
744
745 void printLeft(OutputStream &S) const override {
746 S += "noexcept(";
747 E->print(S);
748 S += ")";
749 }
750};
751
752class DynamicExceptionSpec : public Node {
753 NodeArray Types;
754public:
755 DynamicExceptionSpec(NodeArray Types_)
756 : Node(KDynamicExceptionSpec), Types(Types_) {}
757
758 template<typename Fn> void match(Fn F) const { F(Types); }
759
760 void printLeft(OutputStream &S) const override {
761 S += "throw(";
762 Types.printWithComma(S);
763 S += ')';
764 }
765};
766
767class FunctionEncoding final : public Node {
768 const Node *Ret;
769 const Node *Name;
770 NodeArray Params;
771 const Node *Attrs;
772 Qualifiers CVQuals;
773 FunctionRefQual RefQual;
774
775public:
776 FunctionEncoding(const Node *Ret_, const Node *Name_, NodeArray Params_,
777 const Node *Attrs_, Qualifiers CVQuals_,
778 FunctionRefQual RefQual_)
779 : Node(KFunctionEncoding,
780 /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::No,
781 /*FunctionCache=*/Cache::Yes),
782 Ret(Ret_), Name(Name_), Params(Params_), Attrs(Attrs_),
783 CVQuals(CVQuals_), RefQual(RefQual_) {}
784
785 template<typename Fn> void match(Fn F) const {
786 F(Ret, Name, Params, Attrs, CVQuals, RefQual);
787 }
788
789 Qualifiers getCVQuals() const { return CVQuals; }
790 FunctionRefQual getRefQual() const { return RefQual; }
791 NodeArray getParams() const { return Params; }
792 const Node *getReturnType() const { return Ret; }
793
794 bool hasRHSComponentSlow(OutputStream &) const override { return true; }
795 bool hasFunctionSlow(OutputStream &) const override { return true; }
796
797 const Node *getName() const { return Name; }
798
799 void printLeft(OutputStream &S) const override {
800 if (Ret) {
801 Ret->printLeft(S);
802 if (!Ret->hasRHSComponent(S))
803 S += " ";
804 }
805 Name->print(S);
806 }
807
808 void printRight(OutputStream &S) const override {
809 S += "(";
810 Params.printWithComma(S);
811 S += ")";
812 if (Ret)
813 Ret->printRight(S);
814
815 if (CVQuals & QualConst)
816 S += " const";
817 if (CVQuals & QualVolatile)
818 S += " volatile";
819 if (CVQuals & QualRestrict)
820 S += " restrict";
821
822 if (RefQual == FrefQualLValue)
823 S += " &";
824 else if (RefQual == FrefQualRValue)
825 S += " &&";
826
827 if (Attrs != nullptr)
828 Attrs->print(S);
829 }
830};
831
832class LiteralOperator : public Node {
833 const Node *OpName;
834
835public:
836 LiteralOperator(const Node *OpName_)
837 : Node(KLiteralOperator), OpName(OpName_) {}
838
839 template<typename Fn> void match(Fn F) const { F(OpName); }
840
841 void printLeft(OutputStream &S) const override {
842 S += "operator\"\" ";
843 OpName->print(S);
844 }
845};
846
847class SpecialName final : public Node {
848 const StringView Special;
849 const Node *Child;
850
851public:
852 SpecialName(StringView Special_, const Node *Child_)
853 : Node(KSpecialName), Special(Special_), Child(Child_) {}
854
855 template<typename Fn> void match(Fn F) const { F(Special, Child); }
856
857 void printLeft(OutputStream &S) const override {
858 S += Special;
859 Child->print(S);
860 }
861};
862
863class CtorVtableSpecialName final : public Node {
864 const Node *FirstType;
865 const Node *SecondType;
866
867public:
868 CtorVtableSpecialName(const Node *FirstType_, const Node *SecondType_)
869 : Node(KCtorVtableSpecialName),
870 FirstType(FirstType_), SecondType(SecondType_) {}
871
872 template<typename Fn> void match(Fn F) const { F(FirstType, SecondType); }
873
874 void printLeft(OutputStream &S) const override {
875 S += "construction vtable for ";
876 FirstType->print(S);
877 S += "-in-";
878 SecondType->print(S);
879 }
880};
881
882struct NestedName : Node {
883 Node *Qual;
884 Node *Name;
885
886 NestedName(Node *Qual_, Node *Name_)
887 : Node(KNestedName), Qual(Qual_), Name(Name_) {}
888
889 template<typename Fn> void match(Fn F) const { F(Qual, Name); }
890
891 StringView getBaseName() const override { return Name->getBaseName(); }
892
893 void printLeft(OutputStream &S) const override {
894 Qual->print(S);
895 S += "::";
896 Name->print(S);
897 }
898};
899
900struct LocalName : Node {
901 Node *Encoding;
902 Node *Entity;
903
904 LocalName(Node *Encoding_, Node *Entity_)
905 : Node(KLocalName), Encoding(Encoding_), Entity(Entity_) {}
906
907 template<typename Fn> void match(Fn F) const { F(Encoding, Entity); }
908
909 void printLeft(OutputStream &S) const override {
910 Encoding->print(S);
911 S += "::";
912 Entity->print(S);
913 }
914};
915
916class QualifiedName final : public Node {
917 // qualifier::name
918 const Node *Qualifier;
919 const Node *Name;
920
921public:
922 QualifiedName(const Node *Qualifier_, const Node *Name_)
923 : Node(KQualifiedName), Qualifier(Qualifier_), Name(Name_) {}
924
925 template<typename Fn> void match(Fn F) const { F(Qualifier, Name); }
926
927 StringView getBaseName() const override { return Name->getBaseName(); }
928
929 void printLeft(OutputStream &S) const override {
930 Qualifier->print(S);
931 S += "::";
932 Name->print(S);
933 }
934};
935
936class VectorType final : public Node {
937 const Node *BaseType;
938 const NodeOrString Dimension;
939
940public:
941 VectorType(const Node *BaseType_, NodeOrString Dimension_)
942 : Node(KVectorType), BaseType(BaseType_),
943 Dimension(Dimension_) {}
944
945 template<typename Fn> void match(Fn F) const { F(BaseType, Dimension); }
946
947 void printLeft(OutputStream &S) const override {
948 BaseType->print(S);
949 S += " vector[";
950 if (Dimension.isNode())
951 Dimension.asNode()->print(S);
952 else if (Dimension.isString())
953 S += Dimension.asString();
954 S += "]";
955 }
956};
957
958class PixelVectorType final : public Node {
959 const NodeOrString Dimension;
960
961public:
962 PixelVectorType(NodeOrString Dimension_)
963 : Node(KPixelVectorType), Dimension(Dimension_) {}
964
965 template<typename Fn> void match(Fn F) const { F(Dimension); }
966
967 void printLeft(OutputStream &S) const override {
968 // FIXME: This should demangle as "vector pixel".
969 S += "pixel vector[";
970 S += Dimension.asString();
971 S += "]";
972 }
973};
974
975enum class TemplateParamKind { Type, NonType, Template };
976
977/// An invented name for a template parameter for which we don't have a
978/// corresponding template argument.
979///
980/// This node is created when parsing the <lambda-sig> for a lambda with
981/// explicit template arguments, which might be referenced in the parameter
982/// types appearing later in the <lambda-sig>.
983class SyntheticTemplateParamName final : public Node {
984 TemplateParamKind Kind;
985 unsigned Index;
986
987public:
988 SyntheticTemplateParamName(TemplateParamKind Kind_, unsigned Index_)
989 : Node(KSyntheticTemplateParamName), Kind(Kind_), Index(Index_) {}
990
991 template<typename Fn> void match(Fn F) const { F(Kind, Index); }
992
993 void printLeft(OutputStream &S) const override {
994 switch (Kind) {
995 case TemplateParamKind::Type:
996 S += "$T";
997 break;
998 case TemplateParamKind::NonType:
999 S += "$N";
1000 break;
1001 case TemplateParamKind::Template:
1002 S += "$TT";
1003 break;
1004 }
1005 if (Index > 0)
1006 S << Index - 1;
1007 }
1008};
1009
1010/// A template type parameter declaration, 'typename T'.
1011class TypeTemplateParamDecl final : public Node {
1012 Node *Name;
1013
1014public:
1015 TypeTemplateParamDecl(Node *Name_)
1016 : Node(KTypeTemplateParamDecl, Cache::Yes), Name(Name_) {}
1017
1018 template<typename Fn> void match(Fn F) const { F(Name); }
1019
1020 void printLeft(OutputStream &S) const override {
1021 S += "typename ";
1022 }
1023
1024 void printRight(OutputStream &S) const override {
1025 Name->print(S);
1026 }
1027};
1028
1029/// A non-type template parameter declaration, 'int N'.
1030class NonTypeTemplateParamDecl final : public Node {
1031 Node *Name;
1032 Node *Type;
1033
1034public:
1035 NonTypeTemplateParamDecl(Node *Name_, Node *Type_)
1036 : Node(KNonTypeTemplateParamDecl, Cache::Yes), Name(Name_), Type(Type_) {}
1037
1038 template<typename Fn> void match(Fn F) const { F(Name, Type); }
1039
1040 void printLeft(OutputStream &S) const override {
1041 Type->printLeft(S);
1042 if (!Type->hasRHSComponent(S))
1043 S += " ";
1044 }
1045
1046 void printRight(OutputStream &S) const override {
1047 Name->print(S);
1048 Type->printRight(S);
1049 }
1050};
1051
1052/// A template template parameter declaration,
1053/// 'template<typename T> typename N'.
1054class TemplateTemplateParamDecl final : public Node {
1055 Node *Name;
1056 NodeArray Params;
1057
1058public:
1059 TemplateTemplateParamDecl(Node *Name_, NodeArray Params_)
1060 : Node(KTemplateTemplateParamDecl, Cache::Yes), Name(Name_),
1061 Params(Params_) {}
1062
1063 template<typename Fn> void match(Fn F) const { F(Name, Params); }
1064
1065 void printLeft(OutputStream &S) const override {
1066 S += "template<";
1067 Params.printWithComma(S);
1068 S += "> typename ";
1069 }
1070
1071 void printRight(OutputStream &S) const override {
1072 Name->print(S);
1073 }
1074};
1075
1076/// A template parameter pack declaration, 'typename ...T'.
1077class TemplateParamPackDecl final : public Node {
1078 Node *Param;
1079
1080public:
1081 TemplateParamPackDecl(Node *Param_)
1082 : Node(KTemplateParamPackDecl, Cache::Yes), Param(Param_) {}
1083
1084 template<typename Fn> void match(Fn F) const { F(Param); }
1085
1086 void printLeft(OutputStream &S) const override {
1087 Param->printLeft(S);
1088 S += "...";
1089 }
1090
1091 void printRight(OutputStream &S) const override {
1092 Param->printRight(S);
1093 }
1094};
1095
1096/// An unexpanded parameter pack (either in the expression or type context). If
1097/// this AST is correct, this node will have a ParameterPackExpansion node above
1098/// it.
1099///
1100/// This node is created when some <template-args> are found that apply to an
1101/// <encoding>, and is stored in the TemplateParams table. In order for this to
1102/// appear in the final AST, it has to referenced via a <template-param> (ie,
1103/// T_).
1104class ParameterPack final : public Node {
1105 NodeArray Data;
1106
1107 // Setup OutputStream for a pack expansion unless we're already expanding one.
1108 void initializePackExpansion(OutputStream &S) const {
1109 if (S.CurrentPackMax == std::numeric_limits<unsigned>::max()) {
1110 S.CurrentPackMax = static_cast<unsigned>(Data.size());
1111 S.CurrentPackIndex = 0;
1112 }
1113 }
1114
1115public:
1116 ParameterPack(NodeArray Data_) : Node(KParameterPack), Data(Data_) {
1117 ArrayCache = FunctionCache = RHSComponentCache = Cache::Unknown;
1118 if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
1119 return P->ArrayCache == Cache::No;
1120 }))
1121 ArrayCache = Cache::No;
1122 if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
1123 return P->FunctionCache == Cache::No;
1124 }))
1125 FunctionCache = Cache::No;
1126 if (std::all_of(Data.begin(), Data.end(), [](Node* P) {
1127 return P->RHSComponentCache == Cache::No;
1128 }))
1129 RHSComponentCache = Cache::No;
1130 }
1131
1132 template<typename Fn> void match(Fn F) const { F(Data); }
1133
1134 bool hasRHSComponentSlow(OutputStream &S) const override {
1135 initializePackExpansion(S);
1136 size_t Idx = S.CurrentPackIndex;
1137 return Idx < Data.size() && Data[Idx]->hasRHSComponent(S);
1138 }
1139 bool hasArraySlow(OutputStream &S) const override {
1140 initializePackExpansion(S);
1141 size_t Idx = S.CurrentPackIndex;
1142 return Idx < Data.size() && Data[Idx]->hasArray(S);
1143 }
1144 bool hasFunctionSlow(OutputStream &S) const override {
1145 initializePackExpansion(S);
1146 size_t Idx = S.CurrentPackIndex;
1147 return Idx < Data.size() && Data[Idx]->hasFunction(S);
1148 }
1149 const Node *getSyntaxNode(OutputStream &S) const override {
1150 initializePackExpansion(S);
1151 size_t Idx = S.CurrentPackIndex;
1152 return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this;
1153 }
1154
1155 void printLeft(OutputStream &S) const override {
1156 initializePackExpansion(S);
1157 size_t Idx = S.CurrentPackIndex;
1158 if (Idx < Data.size())
1159 Data[Idx]->printLeft(S);
1160 }
1161 void printRight(OutputStream &S) const override {
1162 initializePackExpansion(S);
1163 size_t Idx = S.CurrentPackIndex;
1164 if (Idx < Data.size())
1165 Data[Idx]->printRight(S);
1166 }
1167};
1168
1169/// A variadic template argument. This node represents an occurrence of
1170/// J<something>E in some <template-args>. It isn't itself unexpanded, unless
1171/// one of it's Elements is. The parser inserts a ParameterPack into the
1172/// TemplateParams table if the <template-args> this pack belongs to apply to an
1173/// <encoding>.
1174class TemplateArgumentPack final : public Node {
1175 NodeArray Elements;
1176public:
1177 TemplateArgumentPack(NodeArray Elements_)
1178 : Node(KTemplateArgumentPack), Elements(Elements_) {}
1179
1180 template<typename Fn> void match(Fn F) const { F(Elements); }
1181
1182 NodeArray getElements() const { return Elements; }
1183
1184 void printLeft(OutputStream &S) const override {
1185 Elements.printWithComma(S);
1186 }
1187};
1188
1189/// A pack expansion. Below this node, there are some unexpanded ParameterPacks
1190/// which each have Child->ParameterPackSize elements.
1191class ParameterPackExpansion final : public Node {
1192 const Node *Child;
1193
1194public:
1195 ParameterPackExpansion(const Node *Child_)
1196 : Node(KParameterPackExpansion), Child(Child_) {}
1197
1198 template<typename Fn> void match(Fn F) const { F(Child); }
1199
1200 const Node *getChild() const { return Child; }
1201
1202 void printLeft(OutputStream &S) const override {
1203 constexpr unsigned Max = std::numeric_limits<unsigned>::max();
1204 SwapAndRestore<unsigned> SavePackIdx(S.CurrentPackIndex, Max);
1205 SwapAndRestore<unsigned> SavePackMax(S.CurrentPackMax, Max);
1206 size_t StreamPos = S.getCurrentPosition();
1207
1208 // Print the first element in the pack. If Child contains a ParameterPack,
1209 // it will set up S.CurrentPackMax and print the first element.
1210 Child->print(S);
1211
1212 // No ParameterPack was found in Child. This can occur if we've found a pack
1213 // expansion on a <function-param>.
1214 if (S.CurrentPackMax == Max) {
1215 S += "...";
1216 return;
1217 }
1218
1219 // We found a ParameterPack, but it has no elements. Erase whatever we may
1220 // of printed.
1221 if (S.CurrentPackMax == 0) {
1222 S.setCurrentPosition(StreamPos);
1223 return;
1224 }
1225
1226 // Else, iterate through the rest of the elements in the pack.
1227 for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) {
1228 S += ", ";
1229 S.CurrentPackIndex = I;
1230 Child->print(S);
1231 }
1232 }
1233};
1234
1235class TemplateArgs final : public Node {
1236 NodeArray Params;
1237
1238public:
1239 TemplateArgs(NodeArray Params_) : Node(KTemplateArgs), Params(Params_) {}
1240
1241 template<typename Fn> void match(Fn F) const { F(Params); }
1242
1243 NodeArray getParams() { return Params; }
1244
1245 void printLeft(OutputStream &S) const override {
1246 S += "<";
1247 Params.printWithComma(S);
1248 if (S.back() == '>')
1249 S += " ";
1250 S += ">";
1251 }
1252};
1253
1254/// A forward-reference to a template argument that was not known at the point
1255/// where the template parameter name was parsed in a mangling.
1256///
1257/// This is created when demangling the name of a specialization of a
1258/// conversion function template:
1259///
1260/// \code
1261/// struct A {
1262/// template<typename T> operator T*();
1263/// };
1264/// \endcode
1265///
1266/// When demangling a specialization of the conversion function template, we
1267/// encounter the name of the template (including the \c T) before we reach
1268/// the template argument list, so we cannot substitute the parameter name
1269/// for the corresponding argument while parsing. Instead, we create a
1270/// \c ForwardTemplateReference node that is resolved after we parse the
1271/// template arguments.
1272struct ForwardTemplateReference : Node {
1273 size_t Index;
1274 Node *Ref = nullptr;
1275
1276 // If we're currently printing this node. It is possible (though invalid) for
1277 // a forward template reference to refer to itself via a substitution. This
1278 // creates a cyclic AST, which will stack overflow printing. To fix this, bail
1279 // out if more than one print* function is active.
1280 mutable bool Printing = false;
1281
1282 ForwardTemplateReference(size_t Index_)
1283 : Node(KForwardTemplateReference, Cache::Unknown, Cache::Unknown,
1284 Cache::Unknown),
1285 Index(Index_) {}
1286
1287 // We don't provide a matcher for these, because the value of the node is
1288 // not determined by its construction parameters, and it generally needs
1289 // special handling.
1290 template<typename Fn> void match(Fn F) const = delete;
1291
1292 bool hasRHSComponentSlow(OutputStream &S) const override {
1293 if (Printing)
1294 return false;
1295 SwapAndRestore<bool> SavePrinting(Printing, true);
1296 return Ref->hasRHSComponent(S);
1297 }
1298 bool hasArraySlow(OutputStream &S) const override {
1299 if (Printing)
1300 return false;
1301 SwapAndRestore<bool> SavePrinting(Printing, true);
1302 return Ref->hasArray(S);
1303 }
1304 bool hasFunctionSlow(OutputStream &S) const override {
1305 if (Printing)
1306 return false;
1307 SwapAndRestore<bool> SavePrinting(Printing, true);
1308 return Ref->hasFunction(S);
1309 }
1310 const Node *getSyntaxNode(OutputStream &S) const override {
1311 if (Printing)
1312 return this;
1313 SwapAndRestore<bool> SavePrinting(Printing, true);
1314 return Ref->getSyntaxNode(S);
1315 }
1316
1317 void printLeft(OutputStream &S) const override {
1318 if (Printing)
1319 return;
1320 SwapAndRestore<bool> SavePrinting(Printing, true);
1321 Ref->printLeft(S);
1322 }
1323 void printRight(OutputStream &S) const override {
1324 if (Printing)
1325 return;
1326 SwapAndRestore<bool> SavePrinting(Printing, true);
1327 Ref->printRight(S);
1328 }
1329};
1330
1331struct NameWithTemplateArgs : Node {
1332 // name<template_args>
1333 Node *Name;
1334 Node *TemplateArgs;
1335
1336 NameWithTemplateArgs(Node *Name_, Node *TemplateArgs_)
1337 : Node(KNameWithTemplateArgs), Name(Name_), TemplateArgs(TemplateArgs_) {}
1338
1339 template<typename Fn> void match(Fn F) const { F(Name, TemplateArgs); }
1340
1341 StringView getBaseName() const override { return Name->getBaseName(); }
1342
1343 void printLeft(OutputStream &S) const override {
1344 Name->print(S);
1345 TemplateArgs->print(S);
1346 }
1347};
1348
1349class GlobalQualifiedName final : public Node {
1350 Node *Child;
1351
1352public:
1353 GlobalQualifiedName(Node* Child_)
1354 : Node(KGlobalQualifiedName), Child(Child_) {}
1355
1356 template<typename Fn> void match(Fn F) const { F(Child); }
1357
1358 StringView getBaseName() const override { return Child->getBaseName(); }
1359
1360 void printLeft(OutputStream &S) const override {
1361 S += "::";
1362 Child->print(S);
1363 }
1364};
1365
1366struct StdQualifiedName : Node {
1367 Node *Child;
1368
1369 StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {}
1370
1371 template<typename Fn> void match(Fn F) const { F(Child); }
1372
1373 StringView getBaseName() const override { return Child->getBaseName(); }
1374
1375 void printLeft(OutputStream &S) const override {
1376 S += "std::";
1377 Child->print(S);
1378 }
1379};
1380
1381enum class SpecialSubKind {
1382 allocator,
1383 basic_string,
1384 string,
1385 istream,
1386 ostream,
1387 iostream,
1388};
1389
1390class ExpandedSpecialSubstitution final : public Node {
1391 SpecialSubKind SSK;
1392
1393public:
1394 ExpandedSpecialSubstitution(SpecialSubKind SSK_)
1395 : Node(KExpandedSpecialSubstitution), SSK(SSK_) {}
1396
1397 template<typename Fn> void match(Fn F) const { F(SSK); }
1398
1399 StringView getBaseName() const override {
1400 switch (SSK) {
1401 case SpecialSubKind::allocator:
1402 return StringView("allocator");
1403 case SpecialSubKind::basic_string:
1404 return StringView("basic_string");
1405 case SpecialSubKind::string:
1406 return StringView("basic_string");
1407 case SpecialSubKind::istream:
1408 return StringView("basic_istream");
1409 case SpecialSubKind::ostream:
1410 return StringView("basic_ostream");
1411 case SpecialSubKind::iostream:
1412 return StringView("basic_iostream");
1413 }
1414 DEMANGLE_UNREACHABLE;
1415 }
1416
1417 void printLeft(OutputStream &S) const override {
1418 switch (SSK) {
1419 case SpecialSubKind::allocator:
1420 S += "std::allocator";
1421 break;
1422 case SpecialSubKind::basic_string:
1423 S += "std::basic_string";
1424 break;
1425 case SpecialSubKind::string:
1426 S += "std::basic_string<char, std::char_traits<char>, "
1427 "std::allocator<char> >";
1428 break;
1429 case SpecialSubKind::istream:
1430 S += "std::basic_istream<char, std::char_traits<char> >";
1431 break;
1432 case SpecialSubKind::ostream:
1433 S += "std::basic_ostream<char, std::char_traits<char> >";
1434 break;
1435 case SpecialSubKind::iostream:
1436 S += "std::basic_iostream<char, std::char_traits<char> >";
1437 break;
1438 }
1439 }
1440};
1441
1442class SpecialSubstitution final : public Node {
1443public:
1444 SpecialSubKind SSK;
1445
1446 SpecialSubstitution(SpecialSubKind SSK_)
1447 : Node(KSpecialSubstitution), SSK(SSK_) {}
1448
1449 template<typename Fn> void match(Fn F) const { F(SSK); }
1450
1451 StringView getBaseName() const override {
1452 switch (SSK) {
1453 case SpecialSubKind::allocator:
1454 return StringView("allocator");
1455 case SpecialSubKind::basic_string:
1456 return StringView("basic_string");
1457 case SpecialSubKind::string:
1458 return StringView("string");
1459 case SpecialSubKind::istream:
1460 return StringView("istream");
1461 case SpecialSubKind::ostream:
1462 return StringView("ostream");
1463 case SpecialSubKind::iostream:
1464 return StringView("iostream");
1465 }
1466 DEMANGLE_UNREACHABLE;
1467 }
1468
1469 void printLeft(OutputStream &S) const override {
1470 switch (SSK) {
1471 case SpecialSubKind::allocator:
1472 S += "std::allocator";
1473 break;
1474 case SpecialSubKind::basic_string:
1475 S += "std::basic_string";
1476 break;
1477 case SpecialSubKind::string:
1478 S += "std::string";
1479 break;
1480 case SpecialSubKind::istream:
1481 S += "std::istream";
1482 break;
1483 case SpecialSubKind::ostream:
1484 S += "std::ostream";
1485 break;
1486 case SpecialSubKind::iostream:
1487 S += "std::iostream";
1488 break;
1489 }
1490 }
1491};
1492
1493class CtorDtorName final : public Node {
1494 const Node *Basename;
1495 const bool IsDtor;
1496 const int Variant;
1497
1498public:
1499 CtorDtorName(const Node *Basename_, bool IsDtor_, int Variant_)
1500 : Node(KCtorDtorName), Basename(Basename_), IsDtor(IsDtor_),
1501 Variant(Variant_) {}
1502
1503 template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
1504
1505 void printLeft(OutputStream &S) const override {
1506 if (IsDtor)
1507 S += "~";
1508 S += Basename->getBaseName();
1509 }
1510};
1511
1512class DtorName : public Node {
1513 const Node *Base;
1514
1515public:
1516 DtorName(const Node *Base_) : Node(KDtorName), Base(Base_) {}
1517
1518 template<typename Fn> void match(Fn F) const { F(Base); }
1519
1520 void printLeft(OutputStream &S) const override {
1521 S += "~";
1522 Base->printLeft(S);
1523 }
1524};
1525
1526class UnnamedTypeName : public Node {
1527 const StringView Count;
1528
1529public:
1530 UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {}
1531
1532 template<typename Fn> void match(Fn F) const { F(Count); }
1533
1534 void printLeft(OutputStream &S) const override {
1535 S += "'unnamed";
1536 S += Count;
1537 S += "\'";
1538 }
1539};
1540
1541class ClosureTypeName : public Node {
1542 NodeArray TemplateParams;
1543 NodeArray Params;
1544 StringView Count;
1545
1546public:
1547 ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_,
1548 StringView Count_)
1549 : Node(KClosureTypeName), TemplateParams(TemplateParams_),
1550 Params(Params_), Count(Count_) {}
1551
1552 template<typename Fn> void match(Fn F) const {
1553 F(TemplateParams, Params, Count);
1554 }
1555
1556 void printDeclarator(OutputStream &S) const {
1557 if (!TemplateParams.empty()) {
1558 S += "<";
1559 TemplateParams.printWithComma(S);
1560 S += ">";
1561 }
1562 S += "(";
1563 Params.printWithComma(S);
1564 S += ")";
1565 }
1566
1567 void printLeft(OutputStream &S) const override {
1568 S += "\'lambda";
1569 S += Count;
1570 S += "\'";
1571 printDeclarator(S);
1572 }
1573};
1574
1575class StructuredBindingName : public Node {
1576 NodeArray Bindings;
1577public:
1578 StructuredBindingName(NodeArray Bindings_)
1579 : Node(KStructuredBindingName), Bindings(Bindings_) {}
1580
1581 template<typename Fn> void match(Fn F) const { F(Bindings); }
1582
1583 void printLeft(OutputStream &S) const override {
1584 S += '[';
1585 Bindings.printWithComma(S);
1586 S += ']';
1587 }
1588};
1589
1590// -- Expression Nodes --
1591
1592class BinaryExpr : public Node {
1593 const Node *LHS;
1594 const StringView InfixOperator;
1595 const Node *RHS;
1596
1597public:
1598 BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_)
1599 : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) {
1600 }
1601
1602 template<typename Fn> void match(Fn F) const { F(LHS, InfixOperator, RHS); }
1603
1604 void printLeft(OutputStream &S) const override {
1605 // might be a template argument expression, then we need to disambiguate
1606 // with parens.
1607 if (InfixOperator == ">")
1608 S += "(";
1609
1610 S += "(";
1611 LHS->print(S);
1612 S += ") ";
1613 S += InfixOperator;
1614 S += " (";
1615 RHS->print(S);
1616 S += ")";
1617
1618 if (InfixOperator == ">")
1619 S += ")";
1620 }
1621};
1622
1623class ArraySubscriptExpr : public Node {
1624 const Node *Op1;
1625 const Node *Op2;
1626
1627public:
1628 ArraySubscriptExpr(const Node *Op1_, const Node *Op2_)
1629 : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {}
1630
1631 template<typename Fn> void match(Fn F) const { F(Op1, Op2); }
1632
1633 void printLeft(OutputStream &S) const override {
1634 S += "(";
1635 Op1->print(S);
1636 S += ")[";
1637 Op2->print(S);
1638 S += "]";
1639 }
1640};
1641
1642class PostfixExpr : public Node {
1643 const Node *Child;
1644 const StringView Operator;
1645
1646public:
1647 PostfixExpr(const Node *Child_, StringView Operator_)
1648 : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {}
1649
1650 template<typename Fn> void match(Fn F) const { F(Child, Operator); }
1651
1652 void printLeft(OutputStream &S) const override {
1653 S += "(";
1654 Child->print(S);
1655 S += ")";
1656 S += Operator;
1657 }
1658};
1659
1660class ConditionalExpr : public Node {
1661 const Node *Cond;
1662 const Node *Then;
1663 const Node *Else;
1664
1665public:
1666 ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_)
1667 : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {}
1668
1669 template<typename Fn> void match(Fn F) const { F(Cond, Then, Else); }
1670
1671 void printLeft(OutputStream &S) const override {
1672 S += "(";
1673 Cond->print(S);
1674 S += ") ? (";
1675 Then->print(S);
1676 S += ") : (";
1677 Else->print(S);
1678 S += ")";
1679 }
1680};
1681
1682class MemberExpr : public Node {
1683 const Node *LHS;
1684 const StringView Kind;
1685 const Node *RHS;
1686
1687public:
1688 MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_)
1689 : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {}
1690
1691 template<typename Fn> void match(Fn F) const { F(LHS, Kind, RHS); }
1692
1693 void printLeft(OutputStream &S) const override {
1694 LHS->print(S);
1695 S += Kind;
1696 RHS->print(S);
1697 }
1698};
1699
1700class EnclosingExpr : public Node {
1701 const StringView Prefix;
1702 const Node *Infix;
1703 const StringView Postfix;
1704
1705public:
1706 EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_)
1707 : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_),
1708 Postfix(Postfix_) {}
1709
1710 template<typename Fn> void match(Fn F) const { F(Prefix, Infix, Postfix); }
1711
1712 void printLeft(OutputStream &S) const override {
1713 S += Prefix;
1714 Infix->print(S);
1715 S += Postfix;
1716 }
1717};
1718
1719class CastExpr : public Node {
1720 // cast_kind<to>(from)
1721 const StringView CastKind;
1722 const Node *To;
1723 const Node *From;
1724
1725public:
1726 CastExpr(StringView CastKind_, const Node *To_, const Node *From_)
1727 : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {}
1728
1729 template<typename Fn> void match(Fn F) const { F(CastKind, To, From); }
1730
1731 void printLeft(OutputStream &S) const override {
1732 S += CastKind;
1733 S += "<";
1734 To->printLeft(S);
1735 S += ">(";
1736 From->printLeft(S);
1737 S += ")";
1738 }
1739};
1740
1741class SizeofParamPackExpr : public Node {
1742 const Node *Pack;
1743
1744public:
1745 SizeofParamPackExpr(const Node *Pack_)
1746 : Node(KSizeofParamPackExpr), Pack(Pack_) {}
1747
1748 template<typename Fn> void match(Fn F) const { F(Pack); }
1749
1750 void printLeft(OutputStream &S) const override {
1751 S += "sizeof...(";
1752 ParameterPackExpansion PPE(Pack);
1753 PPE.printLeft(S);
1754 S += ")";
1755 }
1756};
1757
1758class CallExpr : public Node {
1759 const Node *Callee;
1760 NodeArray Args;
1761
1762public:
1763 CallExpr(const Node *Callee_, NodeArray Args_)
1764 : Node(KCallExpr), Callee(Callee_), Args(Args_) {}
1765
1766 template<typename Fn> void match(Fn F) const { F(Callee, Args); }
1767
1768 void printLeft(OutputStream &S) const override {
1769 Callee->print(S);
1770 S += "(";
1771 Args.printWithComma(S);
1772 S += ")";
1773 }
1774};
1775
1776class NewExpr : public Node {
1777 // new (expr_list) type(init_list)
1778 NodeArray ExprList;
1779 Node *Type;
1780 NodeArray InitList;
1781 bool IsGlobal; // ::operator new ?
1782 bool IsArray; // new[] ?
1783public:
1784 NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_,
1785 bool IsArray_)
1786 : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_),
1787 IsGlobal(IsGlobal_), IsArray(IsArray_) {}
1788
1789 template<typename Fn> void match(Fn F) const {
1790 F(ExprList, Type, InitList, IsGlobal, IsArray);
1791 }
1792
1793 void printLeft(OutputStream &S) const override {
1794 if (IsGlobal)
1795 S += "::operator ";
1796 S += "new";
1797 if (IsArray)
1798 S += "[]";
1799 S += ' ';
1800 if (!ExprList.empty()) {
1801 S += "(";
1802 ExprList.printWithComma(S);
1803 S += ")";
1804 }
1805 Type->print(S);
1806 if (!InitList.empty()) {
1807 S += "(";
1808 InitList.printWithComma(S);
1809 S += ")";
1810 }
1811
1812 }
1813};
1814
1815class DeleteExpr : public Node {
1816 Node *Op;
1817 bool IsGlobal;
1818 bool IsArray;
1819
1820public:
1821 DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_)
1822 : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {}
1823
1824 template<typename Fn> void match(Fn F) const { F(Op, IsGlobal, IsArray); }
1825
1826 void printLeft(OutputStream &S) const override {
1827 if (IsGlobal)
1828 S += "::";
1829 S += "delete";
1830 if (IsArray)
1831 S += "[] ";
1832 Op->print(S);
1833 }
1834};
1835
1836class PrefixExpr : public Node {
1837 StringView Prefix;
1838 Node *Child;
1839
1840public:
1841 PrefixExpr(StringView Prefix_, Node *Child_)
1842 : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {}
1843
1844 template<typename Fn> void match(Fn F) const { F(Prefix, Child); }
1845
1846 void printLeft(OutputStream &S) const override {
1847 S += Prefix;
1848 S += "(";
1849 Child->print(S);
1850 S += ")";
1851 }
1852};
1853
1854class FunctionParam : public Node {
1855 StringView Number;
1856
1857public:
1858 FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {}
1859
1860 template<typename Fn> void match(Fn F) const { F(Number); }
1861
1862 void printLeft(OutputStream &S) const override {
1863 S += "fp";
1864 S += Number;
1865 }
1866};
1867
1868class ConversionExpr : public Node {
1869 const Node *Type;
1870 NodeArray Expressions;
1871
1872public:
1873 ConversionExpr(const Node *Type_, NodeArray Expressions_)
1874 : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {}
1875
1876 template<typename Fn> void match(Fn F) const { F(Type, Expressions); }
1877
1878 void printLeft(OutputStream &S) const override {
1879 S += "(";
1880 Type->print(S);
1881 S += ")(";
1882 Expressions.printWithComma(S);
1883 S += ")";
1884 }
1885};
1886
1887class InitListExpr : public Node {
1888 const Node *Ty;
1889 NodeArray Inits;
1890public:
1891 InitListExpr(const Node *Ty_, NodeArray Inits_)
1892 : Node(KInitListExpr), Ty(Ty_), Inits(Inits_) {}
1893
1894 template<typename Fn> void match(Fn F) const { F(Ty, Inits); }
1895
1896 void printLeft(OutputStream &S) const override {
1897 if (Ty)
1898 Ty->print(S);
1899 S += '{';
1900 Inits.printWithComma(S);
1901 S += '}';
1902 }
1903};
1904
1905class BracedExpr : public Node {
1906 const Node *Elem;
1907 const Node *Init;
1908 bool IsArray;
1909public:
1910 BracedExpr(const Node *Elem_, const Node *Init_, bool IsArray_)
1911 : Node(KBracedExpr), Elem(Elem_), Init(Init_), IsArray(IsArray_) {}
1912
1913 template<typename Fn> void match(Fn F) const { F(Elem, Init, IsArray); }
1914
1915 void printLeft(OutputStream &S) const override {
1916 if (IsArray) {
1917 S += '[';
1918 Elem->print(S);
1919 S += ']';
1920 } else {
1921 S += '.';
1922 Elem->print(S);
1923 }
1924 if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)
1925 S += " = ";
1926 Init->print(S);
1927 }
1928};
1929
1930class BracedRangeExpr : public Node {
1931 const Node *First;
1932 const Node *Last;
1933 const Node *Init;
1934public:
1935 BracedRangeExpr(const Node *First_, const Node *Last_, const Node *Init_)
1936 : Node(KBracedRangeExpr), First(First_), Last(Last_), Init(Init_) {}
1937
1938 template<typename Fn> void match(Fn F) const { F(First, Last, Init); }
1939
1940 void printLeft(OutputStream &S) const override {
1941 S += '[';
1942 First->print(S);
1943 S += " ... ";
1944 Last->print(S);
1945 S += ']';
1946 if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr)
1947 S += " = ";
1948 Init->print(S);
1949 }
1950};
1951
1952class FoldExpr : public Node {
1953 const Node *Pack, *Init;
1954 StringView OperatorName;
1955 bool IsLeftFold;
1956
1957public:
1958 FoldExpr(bool IsLeftFold_, StringView OperatorName_, const Node *Pack_,
1959 const Node *Init_)
1960 : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_),
1961 IsLeftFold(IsLeftFold_) {}
1962
1963 template<typename Fn> void match(Fn F) const {
1964 F(IsLeftFold, OperatorName, Pack, Init);
1965 }
1966
1967 void printLeft(OutputStream &S) const override {
1968 auto PrintPack = [&] {
1969 S += '(';
1970 ParameterPackExpansion(Pack).print(S);
1971 S += ')';
1972 };
1973
1974 S += '(';
1975
1976 if (IsLeftFold) {
1977 // init op ... op pack
1978 if (Init != nullptr) {
1979 Init->print(S);
1980 S += ' ';
1981 S += OperatorName;
1982 S += ' ';
1983 }
1984 // ... op pack
1985 S += "... ";
1986 S += OperatorName;
1987 S += ' ';
1988 PrintPack();
1989 } else { // !IsLeftFold
1990 // pack op ...
1991 PrintPack();
1992 S += ' ';
1993 S += OperatorName;
1994 S += " ...";
1995 // pack op ... op init
1996 if (Init != nullptr) {
1997 S += ' ';
1998 S += OperatorName;
1999 S += ' ';
2000 Init->print(S);
2001 }
2002 }
2003 S += ')';
2004 }
2005};
2006
2007class ThrowExpr : public Node {
2008 const Node *Op;
2009
2010public:
2011 ThrowExpr(const Node *Op_) : Node(KThrowExpr), Op(Op_) {}
2012
2013 template<typename Fn> void match(Fn F) const { F(Op); }
2014
2015 void printLeft(OutputStream &S) const override {
2016 S += "throw ";
2017 Op->print(S);
2018 }
2019};
2020
2021// MSVC __uuidof extension, generated by clang in -fms-extensions mode.
2022class UUIDOfExpr : public Node {
2023 Node *Operand;
2024public:
2025 UUIDOfExpr(Node *Operand_) : Node(KUUIDOfExpr), Operand(Operand_) {}
2026
2027 template<typename Fn> void match(Fn F) const { F(Operand); }
2028
2029 void printLeft(OutputStream &S) const override {
2030 S << "__uuidof(";
2031 Operand->print(S);
2032 S << ")";
2033 }
2034};
2035
2036class BoolExpr : public Node {
2037 bool Value;
2038
2039public:
2040 BoolExpr(bool Value_) : Node(KBoolExpr), Value(Value_) {}
2041
2042 template<typename Fn> void match(Fn F) const { F(Value); }
2043
2044 void printLeft(OutputStream &S) const override {
2045 S += Value ? StringView("true") : StringView("false");
2046 }
2047};
2048
2049class StringLiteral : public Node {
2050 const Node *Type;
2051
2052public:
2053 StringLiteral(const Node *Type_) : Node(KStringLiteral), Type(Type_) {}
2054
2055 template<typename Fn> void match(Fn F) const { F(Type); }
2056
2057 void printLeft(OutputStream &S) const override {
2058 S += "\"<";
2059 Type->print(S);
2060 S += ">\"";
2061 }
2062};
2063
2064class LambdaExpr : public Node {
2065 const Node *Type;
2066
2067public:
2068 LambdaExpr(const Node *Type_) : Node(KLambdaExpr), Type(Type_) {}
2069
2070 template<typename Fn> void match(Fn F) const { F(Type); }
2071
2072 void printLeft(OutputStream &S) const override {
2073 S += "[]";
2074 if (Type->getKind() == KClosureTypeName)
2075 static_cast<const ClosureTypeName *>(Type)->printDeclarator(S);
2076 S += "{...}";
2077 }
2078};
2079
2080class IntegerCastExpr : public Node {
2081 // ty(integer)
2082 const Node *Ty;
2083 StringView Integer;
2084
2085public:
2086 IntegerCastExpr(const Node *Ty_, StringView Integer_)
2087 : Node(KIntegerCastExpr), Ty(Ty_), Integer(Integer_) {}
2088
2089 template<typename Fn> void match(Fn F) const { F(Ty, Integer); }
2090
2091 void printLeft(OutputStream &S) const override {
2092 S += "(";
2093 Ty->print(S);
2094 S += ")";
2095 S += Integer;
2096 }
2097};
2098
2099class IntegerLiteral : public Node {
2100 StringView Type;
2101 StringView Value;
2102
2103public:
2104 IntegerLiteral(StringView Type_, StringView Value_)
2105 : Node(KIntegerLiteral), Type(Type_), Value(Value_) {}
2106
2107 template<typename Fn> void match(Fn F) const { F(Type, Value); }
2108
2109 void printLeft(OutputStream &S) const override {
2110 if (Type.size() > 3) {
2111 S += "(";
2112 S += Type;
2113 S += ")";
2114 }
2115
2116 if (Value[0] == 'n') {
2117 S += "-";
2118 S += Value.dropFront(1);
2119 } else
2120 S += Value;
2121
2122 if (Type.size() <= 3)
2123 S += Type;
2124 }
2125};
2126
2127template <class Float> struct FloatData;
2128
2129namespace float_literal_impl {
2130constexpr Node::Kind getFloatLiteralKind(float *) {
2131 return Node::KFloatLiteral;
2132}
2133constexpr Node::Kind getFloatLiteralKind(double *) {
2134 return Node::KDoubleLiteral;
2135}
2136constexpr Node::Kind getFloatLiteralKind(long double *) {
2137 return Node::KLongDoubleLiteral;
2138}
2139}
2140
2141template <class Float> class FloatLiteralImpl : public Node {
2142 const StringView Contents;
2143
2144 static constexpr Kind KindForClass =
2145 float_literal_impl::getFloatLiteralKind((Float *)nullptr);
2146
2147public:
2148 FloatLiteralImpl(StringView Contents_)
2149 : Node(KindForClass), Contents(Contents_) {}
2150
2151 template<typename Fn> void match(Fn F) const { F(Contents); }
2152
2153 void printLeft(OutputStream &s) const override {
2154 const char *first = Contents.begin();
2155 const char *last = Contents.end() + 1;
2156
2157 const size_t N = FloatData<Float>::mangled_size;
2158 if (static_cast<std::size_t>(last - first) > N) {
2159 last = first + N;
2160 union {
2161 Float value;
2162 char buf[sizeof(Float)];
2163 };
2164 const char *t = first;
2165 char *e = buf;
2166 for (; t != last; ++t, ++e) {
2167 unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0')
2168 : static_cast<unsigned>(*t - 'a' + 10);
2169 ++t;
2170 unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0')
2171 : static_cast<unsigned>(*t - 'a' + 10);
2172 *e = static_cast<char>((d1 << 4) + d0);
2173 }
2174#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
2175 std::reverse(buf, e);
2176#endif
2177 char num[FloatData<Float>::max_demangled_size] = {0};
2178 int n = snprintf(num, sizeof(num), FloatData<Float>::spec, value);
2179 s += StringView(num, num + n);
2180 }
2181 }
2182};
2183
2184using FloatLiteral = FloatLiteralImpl<float>;
2185using DoubleLiteral = FloatLiteralImpl<double>;
2186using LongDoubleLiteral = FloatLiteralImpl<long double>;
2187
2188/// Visit the node. Calls \c F(P), where \c P is the node cast to the
2189/// appropriate derived class.
2190template<typename Fn>
2191void Node::visit(Fn F) const {
2192 switch (K) {
2193#define CASE(X) case K ## X: return F(static_cast<const X*>(this));
2194 FOR_EACH_NODE_KIND(CASE)
2195#undef CASE
2196 }
2197 assert(0 && "unknown mangling node kind");
2198}
2199
2200/// Determine the kind of a node from its type.
2201template<typename NodeT> struct NodeKind;
2202#define SPECIALIZATION(X) \
2203 template<> struct NodeKind<X> { \
2204 static constexpr Node::Kind Kind = Node::K##X; \
2205 static constexpr const char *name() { return #X; } \
2206 };
2207FOR_EACH_NODE_KIND(SPECIALIZATION)
2208#undef SPECIALIZATION
2209
2210#undef FOR_EACH_NODE_KIND
2211
2212template <class T, size_t N>
2213class PODSmallVector {
2214 static_assert(std::is_pod<T>::value,
2215 "T is required to be a plain old data type");
2216
2217 T* First;
2218 T* Last;
2219 T* Cap;
2220 T Inline[N];
2221
2222 bool isInline() const { return First == Inline; }
2223
2224 void clearInline() {
2225 First = Inline;
2226 Last = Inline;
2227 Cap = Inline + N;
2228 }
2229
2230 void reserve(size_t NewCap) {
2231 size_t S = size();
2232 if (isInline()) {
2233 auto* Tmp = static_cast<T*>(std::malloc(NewCap * sizeof(T)));
2234 if (Tmp == nullptr)
2235 std::terminate();
2236 std::copy(First, Last, Tmp);
2237 First = Tmp;
2238 } else {
2239 First = static_cast<T*>(std::realloc(First, NewCap * sizeof(T)));
2240 if (First == nullptr)
2241 std::terminate();
2242 }
2243 Last = First + S;
2244 Cap = First + NewCap;
2245 }
2246
2247public:
2248 PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {}
2249
2250 PODSmallVector(const PODSmallVector&) = delete;
2251 PODSmallVector& operator=(const PODSmallVector&) = delete;
2252
2253 PODSmallVector(PODSmallVector&& Other) : PODSmallVector() {
2254 if (Other.isInline()) {
2255 std::copy(Other.begin(), Other.end(), First);
2256 Last = First + Other.size();
2257 Other.clear();
2258 return;
2259 }
2260
2261 First = Other.First;
2262 Last = Other.Last;
2263 Cap = Other.Cap;
2264 Other.clearInline();
2265 }
2266
2267 PODSmallVector& operator=(PODSmallVector&& Other) {
2268 if (Other.isInline()) {
2269 if (!isInline()) {
2270 std::free(First);
2271 clearInline();
2272 }
2273 std::copy(Other.begin(), Other.end(), First);
2274 Last = First + Other.size();
2275 Other.clear();
2276 return *this;
2277 }
2278
2279 if (isInline()) {
2280 First = Other.First;
2281 Last = Other.Last;
2282 Cap = Other.Cap;
2283 Other.clearInline();
2284 return *this;
2285 }
2286
2287 std::swap(First, Other.First);
2288 std::swap(Last, Other.Last);
2289 std::swap(Cap, Other.Cap);
2290 Other.clear();
2291 return *this;
2292 }
2293
2294 void push_back(const T& Elem) {
2295 if (Last == Cap)
2296 reserve(size() * 2);
2297 *Last++ = Elem;
2298 }
2299
2300 void pop_back() {
2301 assert(Last != First && "Popping empty vector!");
2302 --Last;
2303 }
2304
2305 void dropBack(size_t Index) {
2306 assert(Index <= size() && "dropBack() can't expand!");
2307 Last = First + Index;
2308 }
2309
2310 T* begin() { return First; }
2311 T* end() { return Last; }
2312
2313 bool empty() const { return First == Last; }
2314 size_t size() const { return static_cast<size_t>(Last - First); }
2315 T& back() {
2316 assert(Last != First && "Calling back() on empty vector!");
2317 return *(Last - 1);
2318 }
2319 T& operator[](size_t Index) {
2320 assert(Index < size() && "Invalid access!");
2321 return *(begin() + Index);
2322 }
2323 void clear() { Last = First; }
2324
2325 ~PODSmallVector() {
2326 if (!isInline())
2327 std::free(First);
2328 }
2329};
2330
2331template <typename Derived, typename Alloc> struct AbstractManglingParser {
2332 const char *First;
2333 const char *Last;
2334
2335 // Name stack, this is used by the parser to hold temporary names that were
2336 // parsed. The parser collapses multiple names into new nodes to construct
2337 // the AST. Once the parser is finished, names.size() == 1.
2338 PODSmallVector<Node *, 32> Names;
2339
2340 // Substitution table. Itanium supports name substitutions as a means of
2341 // compression. The string "S42_" refers to the 44nd entry (base-36) in this
2342 // table.
2343 PODSmallVector<Node *, 32> Subs;
2344
2345 using TemplateParamList = PODSmallVector<Node *, 8>;
2346
2347 class ScopedTemplateParamList {
2348 AbstractManglingParser *Parser;
2349 size_t OldNumTemplateParamLists;
2350 TemplateParamList Params;
2351
2352 public:
2353 ScopedTemplateParamList(AbstractManglingParser *Parser)
2354 : Parser(Parser),
2355 OldNumTemplateParamLists(Parser->TemplateParams.size()) {
2356 Parser->TemplateParams.push_back(&Params);
2357 }
2358 ~ScopedTemplateParamList() {
2359 assert(Parser->TemplateParams.size() >= OldNumTemplateParamLists);
2360 Parser->TemplateParams.dropBack(OldNumTemplateParamLists);
2361 }
2362 };
2363
2364 // Template parameter table. Like the above, but referenced like "T42_".
2365 // This has a smaller size compared to Subs and Names because it can be
2366 // stored on the stack.
2367 TemplateParamList OuterTemplateParams;
2368
2369 // Lists of template parameters indexed by template parameter depth,
2370 // referenced like "TL2_4_". If nonempty, element 0 is always
2371 // OuterTemplateParams; inner elements are always template parameter lists of
2372 // lambda expressions. For a generic lambda with no explicit template
2373 // parameter list, the corresponding parameter list pointer will be null.
2374 PODSmallVector<TemplateParamList *, 4> TemplateParams;
2375
2376 // Set of unresolved forward <template-param> references. These can occur in a
2377 // conversion operator's type, and are resolved in the enclosing <encoding>.
2378 PODSmallVector<ForwardTemplateReference *, 4> ForwardTemplateRefs;
2379
2380 bool TryToParseTemplateArgs = true;
2381 bool PermitForwardTemplateReferences = false;
2382 size_t ParsingLambdaParamsAtLevel = (size_t)-1;
2383
2384 unsigned NumSyntheticTemplateParameters[3] = {};
2385
2386 Alloc ASTAllocator;
2387
2388 AbstractManglingParser(const char *First_, const char *Last_)
2389 : First(First_), Last(Last_) {}
2390
2391 Derived &getDerived() { return static_cast<Derived &>(*this); }
2392
2393 void reset(const char *First_, const char *Last_) {
2394 First = First_;
2395 Last = Last_;
2396 Names.clear();
2397 Subs.clear();
2398 TemplateParams.clear();
2399 ParsingLambdaParamsAtLevel = (size_t)-1;
2400 TryToParseTemplateArgs = true;
2401 PermitForwardTemplateReferences = false;
2402 for (int I = 0; I != 3; ++I)
2403 NumSyntheticTemplateParameters[I] = 0;
2404 ASTAllocator.reset();
2405 }
2406
2407 template <class T, class... Args> Node *make(Args &&... args) {
2408 return ASTAllocator.template makeNode<T>(std::forward<Args>(args)...);
2409 }
2410
2411 template <class It> NodeArray makeNodeArray(It begin, It end) {
2412 size_t sz = static_cast<size_t>(end - begin);
2413 void *mem = ASTAllocator.allocateNodeArray(sz);
2414 Node **data = new (mem) Node *[sz];
2415 std::copy(begin, end, data);
2416 return NodeArray(data, sz);
2417 }
2418
2419 NodeArray popTrailingNodeArray(size_t FromPosition) {
2420 assert(FromPosition <= Names.size());
2421 NodeArray res =
2422 makeNodeArray(Names.begin() + (long)FromPosition, Names.end());
2423 Names.dropBack(FromPosition);
2424 return res;
2425 }
2426
2427 bool consumeIf(StringView S) {
2428 if (StringView(First, Last).startsWith(S)) {
2429 First += S.size();
2430 return true;
2431 }
2432 return false;
2433 }
2434
2435 bool consumeIf(char C) {
2436 if (First != Last && *First == C) {
2437 ++First;
2438 return true;
2439 }
2440 return false;
2441 }
2442
2443 char consume() { return First != Last ? *First++ : '\0'; }
2444
2445 char look(unsigned Lookahead = 0) {
2446 if (static_cast<size_t>(Last - First) <= Lookahead)
2447 return '\0';
2448 return First[Lookahead];
2449 }
2450
2451 size_t numLeft() const { return static_cast<size_t>(Last - First); }
2452
2453 StringView parseNumber(bool AllowNegative = false);
2454 Qualifiers parseCVQualifiers();
2455 bool parsePositiveInteger(size_t *Out);
2456 StringView parseBareSourceName();
2457
2458 bool parseSeqId(size_t *Out);
2459 Node *parseSubstitution();
2460 Node *parseTemplateParam();
2461 Node *parseTemplateParamDecl();
2462 Node *parseTemplateArgs(bool TagTemplates = false);
2463 Node *parseTemplateArg();
2464
2465 /// Parse the <expr> production.
2466 Node *parseExpr();
2467 Node *parsePrefixExpr(StringView Kind);
2468 Node *parseBinaryExpr(StringView Kind);
2469 Node *parseIntegerLiteral(StringView Lit);
2470 Node *parseExprPrimary();
2471 template <class Float> Node *parseFloatingLiteral();
2472 Node *parseFunctionParam();
2473 Node *parseNewExpr();
2474 Node *parseConversionExpr();
2475 Node *parseBracedExpr();
2476 Node *parseFoldExpr();
2477
2478 /// Parse the <type> production.
2479 Node *parseType();
2480 Node *parseFunctionType();
2481 Node *parseVectorType();
2482 Node *parseDecltype();
2483 Node *parseArrayType();
2484 Node *parsePointerToMemberType();
2485 Node *parseClassEnumType();
2486 Node *parseQualifiedType();
2487
2488 Node *parseEncoding();
2489 bool parseCallOffset();
2490 Node *parseSpecialName();
2491
2492 /// Holds some extra information about a <name> that is being parsed. This
2493 /// information is only pertinent if the <name> refers to an <encoding>.
2494 struct NameState {
2495 bool CtorDtorConversion = false;
2496 bool EndsWithTemplateArgs = false;
2497 Qualifiers CVQualifiers = QualNone;
2498 FunctionRefQual ReferenceQualifier = FrefQualNone;
2499 size_t ForwardTemplateRefsBegin;
2500
2501 NameState(AbstractManglingParser *Enclosing)
2502 : ForwardTemplateRefsBegin(Enclosing->ForwardTemplateRefs.size()) {}
2503 };
2504
2505 bool resolveForwardTemplateRefs(NameState &State) {
2506 size_t I = State.ForwardTemplateRefsBegin;
2507 size_t E = ForwardTemplateRefs.size();
2508 for (; I < E; ++I) {
2509 size_t Idx = ForwardTemplateRefs[I]->Index;
2510 if (TemplateParams.empty() || !TemplateParams[0] ||
2511 Idx >= TemplateParams[0]->size())
2512 return true;
2513 ForwardTemplateRefs[I]->Ref = (*TemplateParams[0])[Idx];
2514 }
2515 ForwardTemplateRefs.dropBack(State.ForwardTemplateRefsBegin);
2516 return false;
2517 }
2518
2519 /// Parse the <name> production>
2520 Node *parseName(NameState *State = nullptr);
2521 Node *parseLocalName(NameState *State);
2522 Node *parseOperatorName(NameState *State);
2523 Node *parseUnqualifiedName(NameState *State);
2524 Node *parseUnnamedTypeName(NameState *State);
2525 Node *parseSourceName(NameState *State);
2526 Node *parseUnscopedName(NameState *State);
2527 Node *parseNestedName(NameState *State);
2528 Node *parseCtorDtorName(Node *&SoFar, NameState *State);
2529
2530 Node *parseAbiTags(Node *N);
2531
2532 /// Parse the <unresolved-name> production.
2533 Node *parseUnresolvedName();
2534 Node *parseSimpleId();
2535 Node *parseBaseUnresolvedName();
2536 Node *parseUnresolvedType();
2537 Node *parseDestructorName();
2538
2539 /// Top-level entry point into the parser.
2540 Node *parse();
2541};
2542
2543const char* parse_discriminator(const char* first, const char* last);
2544
2545// <name> ::= <nested-name> // N
2546// ::= <local-name> # See Scope Encoding below // Z
2547// ::= <unscoped-template-name> <template-args>
2548// ::= <unscoped-name>
2549//
2550// <unscoped-template-name> ::= <unscoped-name>
2551// ::= <substitution>
2552template <typename Derived, typename Alloc>
2553Node *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) {
2554 consumeIf('L'); // extension
2555
2556 if (look() == 'N')
2557 return getDerived().parseNestedName(State);
2558 if (look() == 'Z')
2559 return getDerived().parseLocalName(State);
2560
2561 // ::= <unscoped-template-name> <template-args>
2562 if (look() == 'S' && look(1) != 't') {
2563 Node *S = getDerived().parseSubstitution();
2564 if (S == nullptr)
2565 return nullptr;
2566 if (look() != 'I')
2567 return nullptr;
2568 Node *TA = getDerived().parseTemplateArgs(State != nullptr);
2569 if (TA == nullptr)
2570 return nullptr;
2571 if (State) State->EndsWithTemplateArgs = true;
2572 return make<NameWithTemplateArgs>(S, TA);
2573 }
2574
2575 Node *N = getDerived().parseUnscopedName(State);
2576 if (N == nullptr)
2577 return nullptr;
2578 // ::= <unscoped-template-name> <template-args>
2579 if (look() == 'I') {
2580 Subs.push_back(N);
2581 Node *TA = getDerived().parseTemplateArgs(State != nullptr);
2582 if (TA == nullptr)
2583 return nullptr;
2584 if (State) State->EndsWithTemplateArgs = true;
2585 return make<NameWithTemplateArgs>(N, TA);
2586 }
2587 // ::= <unscoped-name>
2588 return N;
2589}
2590
2591// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
2592// := Z <function encoding> E s [<discriminator>]
2593// := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
2594template <typename Derived, typename Alloc>
2595Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) {
2596 if (!consumeIf('Z'))
2597 return nullptr;
2598 Node *Encoding = getDerived().parseEncoding();
2599 if (Encoding == nullptr || !consumeIf('E'))
2600 return nullptr;
2601
2602 if (consumeIf('s')) {
2603 First = parse_discriminator(First, Last);
2604 auto *StringLitName = make<NameType>("string literal");
2605 if (!StringLitName)
2606 return nullptr;
2607 return make<LocalName>(Encoding, StringLitName);
2608 }
2609
2610 if (consumeIf('d')) {
2611 parseNumber(true);
2612 if (!consumeIf('_'))
2613 return nullptr;
2614 Node *N = getDerived().parseName(State);
2615 if (N == nullptr)
2616 return nullptr;
2617 return make<LocalName>(Encoding, N);
2618 }
2619
2620 Node *Entity = getDerived().parseName(State);
2621 if (Entity == nullptr)
2622 return nullptr;
2623 First = parse_discriminator(First, Last);
2624 return make<LocalName>(Encoding, Entity);
2625}
2626
2627// <unscoped-name> ::= <unqualified-name>
2628// ::= St <unqualified-name> # ::std::
2629// extension ::= StL<unqualified-name>
2630template <typename Derived, typename Alloc>
2631Node *
2632AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State) {
2633 if (consumeIf("StL") || consumeIf("St")) {
2634 Node *R = getDerived().parseUnqualifiedName(State);
2635 if (R == nullptr)
2636 return nullptr;
2637 return make<StdQualifiedName>(R);
2638 }
2639 return getDerived().parseUnqualifiedName(State);
2640}
2641
2642// <unqualified-name> ::= <operator-name> [abi-tags]
2643// ::= <ctor-dtor-name>
2644// ::= <source-name>
2645// ::= <unnamed-type-name>
2646// ::= DC <source-name>+ E # structured binding declaration
2647template <typename Derived, typename Alloc>
2648Node *
2649AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName(NameState *State) {
2650 // <ctor-dtor-name>s are special-cased in parseNestedName().
2651 Node *Result;
2652 if (look() == 'U')
2653 Result = getDerived().parseUnnamedTypeName(State);
2654 else if (look() >= '1' && look() <= '9')
2655 Result = getDerived().parseSourceName(State);
2656 else if (consumeIf("DC")) {
2657 size_t BindingsBegin = Names.size();
2658 do {
2659 Node *Binding = getDerived().parseSourceName(State);
2660 if (Binding == nullptr)
2661 return nullptr;
2662 Names.push_back(Binding);
2663 } while (!consumeIf('E'));
2664 Result = make<StructuredBindingName>(popTrailingNodeArray(BindingsBegin));
2665 } else
2666 Result = getDerived().parseOperatorName(State);
2667 if (Result != nullptr)
2668 Result = getDerived().parseAbiTags(Result);
2669 return Result;
2670}
2671
2672// <unnamed-type-name> ::= Ut [<nonnegative number>] _
2673// ::= <closure-type-name>
2674//
2675// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _
2676//
2677// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters
2678template <typename Derived, typename Alloc>
2679Node *
2680AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) {
2681 // <template-params> refer to the innermost <template-args>. Clear out any
2682 // outer args that we may have inserted into TemplateParams.
2683 if (State != nullptr)
2684 TemplateParams.clear();
2685
2686 if (consumeIf("Ut")) {
2687 StringView Count = parseNumber();
2688 if (!consumeIf('_'))
2689 return nullptr;
2690 return make<UnnamedTypeName>(Count);
2691 }
2692 if (consumeIf("Ul")) {
2693 SwapAndRestore<size_t> SwapParams(ParsingLambdaParamsAtLevel,
2694 TemplateParams.size());
2695 ScopedTemplateParamList LambdaTemplateParams(this);
2696
2697 size_t ParamsBegin = Names.size();
2698 while (look() == 'T' &&
2699 StringView("yptn").find(look(1)) != StringView::npos) {
2700 Node *T = parseTemplateParamDecl();
2701 if (!T)
2702 return nullptr;
2703 Names.push_back(T);
2704 }
2705 NodeArray TempParams = popTrailingNodeArray(ParamsBegin);
2706
2707 // FIXME: If TempParams is empty and none of the function parameters
2708 // includes 'auto', we should remove LambdaTemplateParams from the
2709 // TemplateParams list. Unfortunately, we don't find out whether there are
2710 // any 'auto' parameters until too late in an example such as:
2711 //
2712 // template<typename T> void f(
2713 // decltype([](decltype([]<typename T>(T v) {}),
2714 // auto) {})) {}
2715 // template<typename T> void f(
2716 // decltype([](decltype([]<typename T>(T w) {}),
2717 // int) {})) {}
2718 //
2719 // Here, the type of v is at level 2 but the type of w is at level 1. We
2720 // don't find this out until we encounter the type of the next parameter.
2721 //
2722 // However, compilers can't actually cope with the former example in
2723 // practice, and it's likely to be made ill-formed in future, so we don't
2724 // need to support it here.
2725 //
2726 // If we encounter an 'auto' in the function parameter types, we will
2727 // recreate a template parameter scope for it, but any intervening lambdas
2728 // will be parsed in the 'wrong' template parameter depth.
2729 if (TempParams.empty())
2730 TemplateParams.pop_back();
2731
2732 if (!consumeIf("vE")) {
2733 do {
2734 Node *P = getDerived().parseType();
2735 if (P == nullptr)
2736 return nullptr;
2737 Names.push_back(P);
2738 } while (!consumeIf('E'));
2739 }
2740 NodeArray Params = popTrailingNodeArray(ParamsBegin);
2741
2742 StringView Count = parseNumber();
2743 if (!consumeIf('_'))
2744 return nullptr;
2745 return make<ClosureTypeName>(TempParams, Params, Count);
2746 }
2747 if (consumeIf("Ub")) {
2748 (void)parseNumber();
2749 if (!consumeIf('_'))
2750 return nullptr;
2751 return make<NameType>("'block-literal'");
2752 }
2753 return nullptr;
2754}
2755
2756// <source-name> ::= <positive length number> <identifier>
2757template <typename Derived, typename Alloc>
2758Node *AbstractManglingParser<Derived, Alloc>::parseSourceName(NameState *) {
2759 size_t Length = 0;
2760 if (parsePositiveInteger(&Length))
2761 return nullptr;
2762 if (numLeft() < Length || Length == 0)
2763 return nullptr;
2764 StringView Name(First, First + Length);
2765 First += Length;
2766 if (Name.startsWith("_GLOBAL__N"))
2767 return make<NameType>("(anonymous namespace)");
2768 return make<NameType>(Name);
2769}
2770
2771// <operator-name> ::= aa # &&
2772// ::= ad # & (unary)
2773// ::= an # &
2774// ::= aN # &=
2775// ::= aS # =
2776// ::= cl # ()
2777// ::= cm # ,
2778// ::= co # ~
2779// ::= cv <type> # (cast)
2780// ::= da # delete[]
2781// ::= de # * (unary)
2782// ::= dl # delete
2783// ::= dv # /
2784// ::= dV # /=
2785// ::= eo # ^
2786// ::= eO # ^=
2787// ::= eq # ==
2788// ::= ge # >=
2789// ::= gt # >
2790// ::= ix # []
2791// ::= le # <=
2792// ::= li <source-name> # operator ""
2793// ::= ls # <<
2794// ::= lS # <<=
2795// ::= lt # <
2796// ::= mi # -
2797// ::= mI # -=
2798// ::= ml # *
2799// ::= mL # *=
2800// ::= mm # -- (postfix in <expression> context)
2801// ::= na # new[]
2802// ::= ne # !=
2803// ::= ng # - (unary)
2804// ::= nt # !
2805// ::= nw # new
2806// ::= oo # ||
2807// ::= or # |
2808// ::= oR # |=
2809// ::= pm # ->*
2810// ::= pl # +
2811// ::= pL # +=
2812// ::= pp # ++ (postfix in <expression> context)
2813// ::= ps # + (unary)
2814// ::= pt # ->
2815// ::= qu # ?
2816// ::= rm # %
2817// ::= rM # %=
2818// ::= rs # >>
2819// ::= rS # >>=
2820// ::= ss # <=> C++2a
2821// ::= v <digit> <source-name> # vendor extended operator
2822template <typename Derived, typename Alloc>
2823Node *
2824AbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) {
2825 switch (look()) {
2826 case 'a':
2827 switch (look(1)) {
2828 case 'a':
2829 First += 2;
2830 return make<NameType>("operator&&");
2831 case 'd':
2832 case 'n':
2833 First += 2;
2834 return make<NameType>("operator&");
2835 case 'N':
2836 First += 2;
2837 return make<NameType>("operator&=");
2838 case 'S':
2839 First += 2;
2840 return make<NameType>("operator=");
2841 }
2842 return nullptr;
2843 case 'c':
2844 switch (look(1)) {
2845 case 'l':
2846 First += 2;
2847 return make<NameType>("operator()");
2848 case 'm':
2849 First += 2;
2850 return make<NameType>("operator,");
2851 case 'o':
2852 First += 2;
2853 return make<NameType>("operator~");
2854 // ::= cv <type> # (cast)
2855 case 'v': {
2856 First += 2;
2857 SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false);
2858 // If we're parsing an encoding, State != nullptr and the conversion
2859 // operators' <type> could have a <template-param> that refers to some
2860 // <template-arg>s further ahead in the mangled name.
2861 SwapAndRestore<bool> SavePermit(PermitForwardTemplateReferences,
2862 PermitForwardTemplateReferences ||
2863 State != nullptr);
2864 Node *Ty = getDerived().parseType();
2865 if (Ty == nullptr)
2866 return nullptr;
2867 if (State) State->CtorDtorConversion = true;
2868 return make<ConversionOperatorType>(Ty);
2869 }
2870 }
2871 return nullptr;
2872 case 'd':
2873 switch (look(1)) {
2874 case 'a':
2875 First += 2;
2876 return make<NameType>("operator delete[]");
2877 case 'e':
2878 First += 2;
2879 return make<NameType>("operator*");
2880 case 'l':
2881 First += 2;
2882 return make<NameType>("operator delete");
2883 case 'v':
2884 First += 2;
2885 return make<NameType>("operator/");
2886 case 'V':
2887 First += 2;
2888 return make<NameType>("operator/=");
2889 }
2890 return nullptr;
2891 case 'e':
2892 switch (look(1)) {
2893 case 'o':
2894 First += 2;
2895 return make<NameType>("operator^");
2896 case 'O':
2897 First += 2;
2898 return make<NameType>("operator^=");
2899 case 'q':
2900 First += 2;
2901 return make<NameType>("operator==");
2902 }
2903 return nullptr;
2904 case 'g':
2905 switch (look(1)) {
2906 case 'e':
2907 First += 2;
2908 return make<NameType>("operator>=");
2909 case 't':
2910 First += 2;
2911 return make<NameType>("operator>");
2912 }
2913 return nullptr;
2914 case 'i':
2915 if (look(1) == 'x') {
2916 First += 2;
2917 return make<NameType>("operator[]");
2918 }
2919 return nullptr;
2920 case 'l':
2921 switch (look(1)) {
2922 case 'e':
2923 First += 2;
2924 return make<NameType>("operator<=");
2925 // ::= li <source-name> # operator ""
2926 case 'i': {
2927 First += 2;
2928 Node *SN = getDerived().parseSourceName(State);
2929 if (SN == nullptr)
2930 return nullptr;
2931 return make<LiteralOperator>(SN);
2932 }
2933 case 's':
2934 First += 2;
2935 return make<NameType>("operator<<");
2936 case 'S':
2937 First += 2;
2938 return make<NameType>("operator<<=");
2939 case 't':
2940 First += 2;
2941 return make<NameType>("operator<");
2942 }
2943 return nullptr;
2944 case 'm':
2945 switch (look(1)) {
2946 case 'i':
2947 First += 2;
2948 return make<NameType>("operator-");
2949 case 'I':
2950 First += 2;
2951 return make<NameType>("operator-=");
2952 case 'l':
2953 First += 2;
2954 return make<NameType>("operator*");
2955 case 'L':
2956 First += 2;
2957 return make<NameType>("operator*=");
2958 case 'm':
2959 First += 2;
2960 return make<NameType>("operator--");
2961 }
2962 return nullptr;
2963 case 'n':
2964 switch (look(1)) {
2965 case 'a':
2966 First += 2;
2967 return make<NameType>("operator new[]");
2968 case 'e':
2969 First += 2;
2970 return make<NameType>("operator!=");
2971 case 'g':
2972 First += 2;
2973 return make<NameType>("operator-");
2974 case 't':
2975 First += 2;
2976 return make<NameType>("operator!");
2977 case 'w':
2978 First += 2;
2979 return make<NameType>("operator new");
2980 }
2981 return nullptr;
2982 case 'o':
2983 switch (look(1)) {
2984 case 'o':
2985 First += 2;
2986 return make<NameType>("operator||");
2987 case 'r':
2988 First += 2;
2989 return make<NameType>("operator|");
2990 case 'R':
2991 First += 2;
2992 return make<NameType>("operator|=");
2993 }
2994 return nullptr;
2995 case 'p':
2996 switch (look(1)) {
2997 case 'm':
2998 First += 2;
2999 return make<NameType>("operator->*");
3000 case 'l':
3001 First += 2;
3002 return make<NameType>("operator+");
3003 case 'L':
3004 First += 2;
3005 return make<NameType>("operator+=");
3006 case 'p':
3007 First += 2;
3008 return make<NameType>("operator++");
3009 case 's':
3010 First += 2;
3011 return make<NameType>("operator+");
3012 case 't':
3013 First += 2;
3014 return make<NameType>("operator->");
3015 }
3016 return nullptr;
3017 case 'q':
3018 if (look(1) == 'u') {
3019 First += 2;
3020 return make<NameType>("operator?");
3021 }
3022 return nullptr;
3023 case 'r':
3024 switch (look(1)) {
3025 case 'm':
3026 First += 2;
3027 return make<NameType>("operator%");
3028 case 'M':
3029 First += 2;
3030 return make<NameType>("operator%=");
3031 case 's':
3032 First += 2;
3033 return make<NameType>("operator>>");
3034 case 'S':
3035 First += 2;
3036 return make<NameType>("operator>>=");
3037 }
3038 return nullptr;
3039 case 's':
3040 if (look(1) == 's') {
3041 First += 2;
3042 return make<NameType>("operator<=>");
3043 }
3044 return nullptr;
3045 // ::= v <digit> <source-name> # vendor extended operator
3046 case 'v':
3047 if (std::isdigit(look(1))) {
3048 First += 2;
3049 Node *SN = getDerived().parseSourceName(State);
3050 if (SN == nullptr)
3051 return nullptr;
3052 return make<ConversionOperatorType>(SN);
3053 }
3054 return nullptr;
3055 }
3056 return nullptr;
3057}
3058
3059// <ctor-dtor-name> ::= C1 # complete object constructor
3060// ::= C2 # base object constructor
3061// ::= C3 # complete object allocating constructor
3062// extension ::= C4 # gcc old-style "[unified]" constructor
3063// extension ::= C5 # the COMDAT used for ctors
3064// ::= D0 # deleting destructor
3065// ::= D1 # complete object destructor
3066// ::= D2 # base object destructor
3067// extension ::= D4 # gcc old-style "[unified]" destructor
3068// extension ::= D5 # the COMDAT used for dtors
3069template <typename Derived, typename Alloc>
3070Node *
3071AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar,
3072 NameState *State) {
3073 if (SoFar->getKind() == Node::KSpecialSubstitution) {
3074 auto SSK = static_cast<SpecialSubstitution *>(SoFar)->SSK;
3075 switch (SSK) {
3076 case SpecialSubKind::string:
3077 case SpecialSubKind::istream:
3078 case SpecialSubKind::ostream:
3079 case SpecialSubKind::iostream:
3080 SoFar = make<ExpandedSpecialSubstitution>(SSK);
3081 if (!SoFar)
3082 return nullptr;
3083 break;
3084 default:
3085 break;
3086 }
3087 }
3088
3089 if (consumeIf('C')) {
3090 bool IsInherited = consumeIf('I');
3091 if (look() != '1' && look() != '2' && look() != '3' && look() != '4' &&
3092 look() != '5')
3093 return nullptr;
3094 int Variant = look() - '0';
3095 ++First;
3096 if (State) State->CtorDtorConversion = true;
3097 if (IsInherited) {
3098 if (getDerived().parseName(State) == nullptr)
3099 return nullptr;
3100 }
3101 return make<CtorDtorName>(SoFar, /*IsDtor=*/false, Variant);
3102 }
3103
3104 if (look() == 'D' && (look(1) == '0' || look(1) == '1' || look(1) == '2' ||
3105 look(1) == '4' || look(1) == '5')) {
3106 int Variant = look(1) - '0';
3107 First += 2;
3108 if (State) State->CtorDtorConversion = true;
3109 return make<CtorDtorName>(SoFar, /*IsDtor=*/true, Variant);
3110 }
3111
3112 return nullptr;
3113}
3114
3115// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
3116// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E
3117//
3118// <prefix> ::= <prefix> <unqualified-name>
3119// ::= <template-prefix> <template-args>
3120// ::= <template-param>
3121// ::= <decltype>
3122// ::= # empty
3123// ::= <substitution>
3124// ::= <prefix> <data-member-prefix>
3125// extension ::= L
3126//
3127// <data-member-prefix> := <member source-name> [<template-args>] M
3128//
3129// <template-prefix> ::= <prefix> <template unqualified-name>
3130// ::= <template-param>
3131// ::= <substitution>
3132template <typename Derived, typename Alloc>
3133Node *
3134AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) {
3135 if (!consumeIf('N'))
3136 return nullptr;
3137
3138 Qualifiers CVTmp = parseCVQualifiers();
3139 if (State) State->CVQualifiers = CVTmp;
3140
3141 if (consumeIf('O')) {
3142 if (State) State->ReferenceQualifier = FrefQualRValue;
3143 } else if (consumeIf('R')) {
3144 if (State) State->ReferenceQualifier = FrefQualLValue;
3145 } else
3146 if (State) State->ReferenceQualifier = FrefQualNone;
3147
3148 Node *SoFar = nullptr;
3149 auto PushComponent = [&](Node *Comp) {
3150 if (!Comp) return false;
3151 if (SoFar) SoFar = make<NestedName>(SoFar, Comp);
3152 else SoFar = Comp;
3153 if (State) State->EndsWithTemplateArgs = false;
3154 return SoFar != nullptr;
3155 };
3156
3157 if (consumeIf("St")) {
3158 SoFar = make<NameType>("std");
3159 if (!SoFar)
3160 return nullptr;
3161 }
3162
3163 while (!consumeIf('E')) {
3164 consumeIf('L'); // extension
3165
3166 // <data-member-prefix> := <member source-name> [<template-args>] M
3167 if (consumeIf('M')) {
3168 if (SoFar == nullptr)
3169 return nullptr;
3170 continue;
3171 }
3172
3173 // ::= <template-param>
3174 if (look() == 'T') {
3175 if (!PushComponent(getDerived().parseTemplateParam()))
3176 return nullptr;
3177 Subs.push_back(SoFar);
3178 continue;
3179 }
3180
3181 // ::= <template-prefix> <template-args>
3182 if (look() == 'I') {
3183 Node *TA = getDerived().parseTemplateArgs(State != nullptr);
3184 if (TA == nullptr || SoFar == nullptr)
3185 return nullptr;
3186 SoFar = make<NameWithTemplateArgs>(SoFar, TA);
3187 if (!SoFar)
3188 return nullptr;
3189 if (State) State->EndsWithTemplateArgs = true;
3190 Subs.push_back(SoFar);
3191 continue;
3192 }
3193
3194 // ::= <decltype>
3195 if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) {
3196 if (!PushComponent(getDerived().parseDecltype()))
3197 return nullptr;
3198 Subs.push_back(SoFar);
3199 continue;
3200 }
3201
3202 // ::= <substitution>
3203 if (look() == 'S' && look(1) != 't') {
3204 Node *S = getDerived().parseSubstitution();
3205 if (!PushComponent(S))
3206 return nullptr;
3207 if (SoFar != S)
3208 Subs.push_back(S);
3209 continue;
3210 }
3211
3212 // Parse an <unqualified-name> thats actually a <ctor-dtor-name>.
3213 if (look() == 'C' || (look() == 'D' && look(1) != 'C')) {
3214 if (SoFar == nullptr)
3215 return nullptr;
3216 if (!PushComponent(getDerived().parseCtorDtorName(SoFar, State)))
3217 return nullptr;
3218 SoFar = getDerived().parseAbiTags(SoFar);
3219 if (SoFar == nullptr)
3220 return nullptr;
3221 Subs.push_back(SoFar);
3222 continue;
3223 }
3224
3225 // ::= <prefix> <unqualified-name>
3226 if (!PushComponent(getDerived().parseUnqualifiedName(State)))
3227 return nullptr;
3228 Subs.push_back(SoFar);
3229 }
3230
3231 if (SoFar == nullptr || Subs.empty())
3232 return nullptr;
3233
3234 Subs.pop_back();
3235 return SoFar;
3236}
3237
3238// <simple-id> ::= <source-name> [ <template-args> ]
3239template <typename Derived, typename Alloc>
3240Node *AbstractManglingParser<Derived, Alloc>::parseSimpleId() {
3241 Node *SN = getDerived().parseSourceName(/*NameState=*/nullptr);
3242 if (SN == nullptr)
3243 return nullptr;
3244 if (look() == 'I') {
3245 Node *TA = getDerived().parseTemplateArgs();
3246 if (TA == nullptr)
3247 return nullptr;
3248 return make<NameWithTemplateArgs>(SN, TA);
3249 }
3250 return SN;
3251}
3252
3253// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f())
3254// ::= <simple-id> # e.g., ~A<2*N>
3255template <typename Derived, typename Alloc>
3256Node *AbstractManglingParser<Derived, Alloc>::parseDestructorName() {
3257 Node *Result;
3258 if (std::isdigit(look()))
3259 Result = getDerived().parseSimpleId();
3260 else
3261 Result = getDerived().parseUnresolvedType();
3262 if (Result == nullptr)
3263 return nullptr;
3264 return make<DtorName>(Result);
3265}
3266
3267// <unresolved-type> ::= <template-param>
3268// ::= <decltype>
3269// ::= <substitution>
3270template <typename Derived, typename Alloc>
3271Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedType() {
3272 if (look() == 'T') {
3273 Node *TP = getDerived().parseTemplateParam();
3274 if (TP == nullptr)
3275 return nullptr;
3276 Subs.push_back(TP);
3277 return TP;
3278 }
3279 if (look() == 'D') {
3280 Node *DT = getDerived().parseDecltype();
3281 if (DT == nullptr)
3282 return nullptr;
3283 Subs.push_back(DT);
3284 return DT;
3285 }
3286 return getDerived().parseSubstitution();
3287}
3288
3289// <base-unresolved-name> ::= <simple-id> # unresolved name
3290// extension ::= <operator-name> # unresolved operator-function-id
3291// extension ::= <operator-name> <template-args> # unresolved operator template-id
3292// ::= on <operator-name> # unresolved operator-function-id
3293// ::= on <operator-name> <template-args> # unresolved operator template-id
3294// ::= dn <destructor-name> # destructor or pseudo-destructor;
3295// # e.g. ~X or ~X<N-1>
3296template <typename Derived, typename Alloc>
3297Node *AbstractManglingParser<Derived, Alloc>::parseBaseUnresolvedName() {
3298 if (std::isdigit(look()))
3299 return getDerived().parseSimpleId();
3300
3301 if (consumeIf("dn"))
3302 return getDerived().parseDestructorName();
3303
3304 consumeIf("on");
3305
3306 Node *Oper = getDerived().parseOperatorName(/*NameState=*/nullptr);
3307 if (Oper == nullptr)
3308 return nullptr;
3309 if (look() == 'I') {
3310 Node *TA = getDerived().parseTemplateArgs();
3311 if (TA == nullptr)
3312 return nullptr;
3313 return make<NameWithTemplateArgs>(Oper, TA);
3314 }
3315 return Oper;
3316}
3317
3318// <unresolved-name>
3319// extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>
3320// ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
3321// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
3322// # A::x, N::y, A<T>::z; "gs" means leading "::"
3323// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x
3324// extension ::= sr <unresolved-type> <template-args> <base-unresolved-name>
3325// # T::N::x /decltype(p)::N::x
3326// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
3327//
3328// <unresolved-qualifier-level> ::= <simple-id>
3329template <typename Derived, typename Alloc>
3330Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName() {
3331 Node *SoFar = nullptr;
3332
3333 // srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name>
3334 // srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
3335 if (consumeIf("srN")) {
3336 SoFar = getDerived().parseUnresolvedType();
3337 if (SoFar == nullptr)
3338 return nullptr;
3339
3340 if (look() == 'I') {
3341 Node *TA = getDerived().parseTemplateArgs();
3342 if (TA == nullptr)
3343 return nullptr;
3344 SoFar = make<NameWithTemplateArgs>(SoFar, TA);
3345 if (!SoFar)
3346 return nullptr;
3347 }
3348
3349 while (!consumeIf('E')) {
3350 Node *Qual = getDerived().parseSimpleId();
3351 if (Qual == nullptr)
3352 return nullptr;
3353 SoFar = make<QualifiedName>(SoFar, Qual);
3354 if (!SoFar)
3355 return nullptr;
3356 }
3357
3358 Node *Base = getDerived().parseBaseUnresolvedName();
3359 if (Base == nullptr)
3360 return nullptr;
3361 return make<QualifiedName>(SoFar, Base);
3362 }
3363
3364 bool Global = consumeIf("gs");
3365
3366 // [gs] <base-unresolved-name> # x or (with "gs") ::x
3367 if (!consumeIf("sr")) {
3368 SoFar = getDerived().parseBaseUnresolvedName();
3369 if (SoFar == nullptr)
3370 return nullptr;
3371 if (Global)
3372 SoFar = make<GlobalQualifiedName>(SoFar);
3373 return SoFar;
3374 }
3375
3376 // [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
3377 if (std::isdigit(look())) {
3378 do {
3379 Node *Qual = getDerived().parseSimpleId();
3380 if (Qual == nullptr)
3381 return nullptr;
3382 if (SoFar)
3383 SoFar = make<QualifiedName>(SoFar, Qual);
3384 else if (Global)
3385 SoFar = make<GlobalQualifiedName>(Qual);
3386 else
3387 SoFar = Qual;
3388 if (!SoFar)
3389 return nullptr;
3390 } while (!consumeIf('E'));
3391 }
3392 // sr <unresolved-type> <base-unresolved-name>
3393 // sr <unresolved-type> <template-args> <base-unresolved-name>
3394 else {
3395 SoFar = getDerived().parseUnresolvedType();
3396 if (SoFar == nullptr)
3397 return nullptr;
3398
3399 if (look() == 'I') {
3400 Node *TA = getDerived().parseTemplateArgs();
3401 if (TA == nullptr)
3402 return nullptr;
3403 SoFar = make<NameWithTemplateArgs>(SoFar, TA);
3404 if (!SoFar)
3405 return nullptr;
3406 }
3407 }
3408
3409 assert(SoFar != nullptr);
3410
3411 Node *Base = getDerived().parseBaseUnresolvedName();
3412 if (Base == nullptr)
3413 return nullptr;
3414 return make<QualifiedName>(SoFar, Base);
3415}
3416
3417// <abi-tags> ::= <abi-tag> [<abi-tags>]
3418// <abi-tag> ::= B <source-name>
3419template <typename Derived, typename Alloc>
3420Node *AbstractManglingParser<Derived, Alloc>::parseAbiTags(Node *N) {
3421 while (consumeIf('B')) {
3422 StringView SN = parseBareSourceName();
3423 if (SN.empty())
3424 return nullptr;
3425 N = make<AbiTagAttr>(N, SN);
3426 if (!N)
3427 return nullptr;
3428 }
3429 return N;
3430}
3431
3432// <number> ::= [n] <non-negative decimal integer>
3433template <typename Alloc, typename Derived>
3434StringView
3435AbstractManglingParser<Alloc, Derived>::parseNumber(bool AllowNegative) {
3436 const char *Tmp = First;
3437 if (AllowNegative)
3438 consumeIf('n');
3439 if (numLeft() == 0 || !std::isdigit(*First))
3440 return StringView();
3441 while (numLeft() != 0 && std::isdigit(*First))
3442 ++First;
3443 return StringView(Tmp, First);
3444}
3445
3446// <positive length number> ::= [0-9]*
3447template <typename Alloc, typename Derived>
3448bool AbstractManglingParser<Alloc, Derived>::parsePositiveInteger(size_t *Out) {
3449 *Out = 0;
3450 if (look() < '0' || look() > '9')
3451 return true;
3452 while (look() >= '0' && look() <= '9') {
3453 *Out *= 10;
3454 *Out += static_cast<size_t>(consume() - '0');
3455 }
3456 return false;
3457}
3458
3459template <typename Alloc, typename Derived>
3460StringView AbstractManglingParser<Alloc, Derived>::parseBareSourceName() {
3461 size_t Int = 0;
3462 if (parsePositiveInteger(&Int) || numLeft() < Int)
3463 return StringView();
3464 StringView R(First, First + Int);
3465 First += Int;
3466 return R;
3467}
3468
3469// <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y] <bare-function-type> [<ref-qualifier>] E
3470//
3471// <exception-spec> ::= Do # non-throwing exception-specification (e.g., noexcept, throw())
3472// ::= DO <expression> E # computed (instantiation-dependent) noexcept
3473// ::= Dw <type>+ E # dynamic exception specification with instantiation-dependent types
3474//
3475// <ref-qualifier> ::= R # & ref-qualifier
3476// <ref-qualifier> ::= O # && ref-qualifier
3477template <typename Derived, typename Alloc>
3478Node *AbstractManglingParser<Derived, Alloc>::parseFunctionType() {
3479 Qualifiers CVQuals = parseCVQualifiers();
3480
3481 Node *ExceptionSpec = nullptr;
3482 if (consumeIf("Do")) {
3483 ExceptionSpec = make<NameType>("noexcept");
3484 if (!ExceptionSpec)
3485 return nullptr;
3486 } else if (consumeIf("DO")) {
3487 Node *E = getDerived().parseExpr();
3488 if (E == nullptr || !consumeIf('E'))
3489 return nullptr;
3490 ExceptionSpec = make<NoexceptSpec>(E);
3491 if (!ExceptionSpec)
3492 return nullptr;
3493 } else if (consumeIf("Dw")) {
3494 size_t SpecsBegin = Names.size();
3495 while (!consumeIf('E')) {
3496 Node *T = getDerived().parseType();
3497 if (T == nullptr)
3498 return nullptr;
3499 Names.push_back(T);
3500 }
3501 ExceptionSpec =
3502 make<DynamicExceptionSpec>(popTrailingNodeArray(SpecsBegin));
3503 if (!ExceptionSpec)
3504 return nullptr;
3505 }
3506
3507 consumeIf("Dx"); // transaction safe
3508
3509 if (!consumeIf('F'))
3510 return nullptr;
3511 consumeIf('Y'); // extern "C"
3512 Node *ReturnType = getDerived().parseType();
3513 if (ReturnType == nullptr)
3514 return nullptr;
3515
3516 FunctionRefQual ReferenceQualifier = FrefQualNone;
3517 size_t ParamsBegin = Names.size();
3518 while (true) {
3519 if (consumeIf('E'))
3520 break;
3521 if (consumeIf('v'))
3522 continue;
3523 if (consumeIf("RE")) {
3524 ReferenceQualifier = FrefQualLValue;
3525 break;
3526 }
3527 if (consumeIf("OE")) {
3528 ReferenceQualifier = FrefQualRValue;
3529 break;
3530 }
3531 Node *T = getDerived().parseType();
3532 if (T == nullptr)
3533 return nullptr;
3534 Names.push_back(T);
3535 }
3536
3537 NodeArray Params = popTrailingNodeArray(ParamsBegin);
3538 return make<FunctionType>(ReturnType, Params, CVQuals,
3539 ReferenceQualifier, ExceptionSpec);
3540}
3541
3542// extension:
3543// <vector-type> ::= Dv <positive dimension number> _ <extended element type>
3544// ::= Dv [<dimension expression>] _ <element type>
3545// <extended element type> ::= <element type>
3546// ::= p # AltiVec vector pixel
3547template <typename Derived, typename Alloc>
3548Node *AbstractManglingParser<Derived, Alloc>::parseVectorType() {
3549 if (!consumeIf("Dv"))
3550 return nullptr;
3551 if (look() >= '1' && look() <= '9') {
3552 StringView DimensionNumber = parseNumber();
3553 if (!consumeIf('_'))
3554 return nullptr;
3555 if (consumeIf('p'))
3556 return make<PixelVectorType>(DimensionNumber);
3557 Node *ElemType = getDerived().parseType();
3558 if (ElemType == nullptr)
3559 return nullptr;
3560 return make<VectorType>(ElemType, DimensionNumber);
3561 }
3562
3563 if (!consumeIf('_')) {
3564 Node *DimExpr = getDerived().parseExpr();
3565 if (!DimExpr)
3566 return nullptr;
3567 if (!consumeIf('_'))
3568 return nullptr;
3569 Node *ElemType = getDerived().parseType();
3570 if (!ElemType)
3571 return nullptr;
3572 return make<VectorType>(ElemType, DimExpr);
3573 }
3574 Node *ElemType = getDerived().parseType();
3575 if (!ElemType)
3576 return nullptr;
3577 return make<VectorType>(ElemType, StringView());
3578}
3579
3580// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x)
3581// ::= DT <expression> E # decltype of an expression (C++0x)
3582template <typename Derived, typename Alloc>
3583Node *AbstractManglingParser<Derived, Alloc>::parseDecltype() {
3584 if (!consumeIf('D'))
3585 return nullptr;
3586 if (!consumeIf('t') && !consumeIf('T'))
3587 return nullptr;
3588 Node *E = getDerived().parseExpr();
3589 if (E == nullptr)
3590 return nullptr;
3591 if (!consumeIf('E'))
3592 return nullptr;
3593 return make<EnclosingExpr>("decltype(", E, ")");
3594}
3595
3596// <array-type> ::= A <positive dimension number> _ <element type>
3597// ::= A [<dimension expression>] _ <element type>
3598template <typename Derived, typename Alloc>
3599Node *AbstractManglingParser<Derived, Alloc>::parseArrayType() {
3600 if (!consumeIf('A'))
3601 return nullptr;
3602
3603 NodeOrString Dimension;
3604
3605 if (std::isdigit(look())) {
3606 Dimension = parseNumber();
3607 if (!consumeIf('_'))
3608 return nullptr;
3609 } else if (!consumeIf('_')) {
3610 Node *DimExpr = getDerived().parseExpr();
3611 if (DimExpr == nullptr)
3612 return nullptr;
3613 if (!consumeIf('_'))
3614 return nullptr;
3615 Dimension = DimExpr;
3616 }
3617
3618 Node *Ty = getDerived().parseType();
3619 if (Ty == nullptr)
3620 return nullptr;
3621 return make<ArrayType>(Ty, Dimension);
3622}
3623
3624// <pointer-to-member-type> ::= M <class type> <member type>
3625template <typename Derived, typename Alloc>
3626Node *AbstractManglingParser<Derived, Alloc>::parsePointerToMemberType() {
3627 if (!consumeIf('M'))
3628 return nullptr;
3629 Node *ClassType = getDerived().parseType();
3630 if (ClassType == nullptr)
3631 return nullptr;
3632 Node *MemberType = getDerived().parseType();
3633 if (MemberType == nullptr)
3634 return nullptr;
3635 return make<PointerToMemberType>(ClassType, MemberType);
3636}
3637
3638// <class-enum-type> ::= <name> # non-dependent type name, dependent type name, or dependent typename-specifier
3639// ::= Ts <name> # dependent elaborated type specifier using 'struct' or 'class'
3640// ::= Tu <name> # dependent elaborated type specifier using 'union'
3641// ::= Te <name> # dependent elaborated type specifier using 'enum'
3642template <typename Derived, typename Alloc>
3643Node *AbstractManglingParser<Derived, Alloc>::parseClassEnumType() {
3644 StringView ElabSpef;
3645 if (consumeIf("Ts"))
3646 ElabSpef = "struct";
3647 else if (consumeIf("Tu"))
3648 ElabSpef = "union";
3649 else if (consumeIf("Te"))
3650 ElabSpef = "enum";
3651
3652 Node *Name = getDerived().parseName();
3653 if (Name == nullptr)
3654 return nullptr;
3655
3656 if (!ElabSpef.empty())
3657 return make<ElaboratedTypeSpefType>(ElabSpef, Name);
3658
3659 return Name;
3660}
3661
3662// <qualified-type> ::= <qualifiers> <type>
3663// <qualifiers> ::= <extended-qualifier>* <CV-qualifiers>
3664// <extended-qualifier> ::= U <source-name> [<template-args>] # vendor extended type qualifier
3665template <typename Derived, typename Alloc>
3666Node *AbstractManglingParser<Derived, Alloc>::parseQualifiedType() {
3667 if (consumeIf('U')) {
3668 StringView Qual = parseBareSourceName();
3669 if (Qual.empty())
3670 return nullptr;
3671
3672 // FIXME parse the optional <template-args> here!
3673
3674 // extension ::= U <objc-name> <objc-type> # objc-type<identifier>
3675 if (Qual.startsWith("objcproto")) {
3676 StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto"));
3677 StringView Proto;
3678 {
3679 SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()),
3680 SaveLast(Last, ProtoSourceName.end());
3681 Proto = parseBareSourceName();
3682 }
3683 if (Proto.empty())
3684 return nullptr;
3685 Node *Child = getDerived().parseQualifiedType();
3686 if (Child == nullptr)
3687 return nullptr;
3688 return make<ObjCProtoName>(Child, Proto);
3689 }
3690
3691 Node *Child = getDerived().parseQualifiedType();
3692 if (Child == nullptr)
3693 return nullptr;
3694 return make<VendorExtQualType>(Child, Qual);
3695 }
3696
3697 Qualifiers Quals = parseCVQualifiers();
3698 Node *Ty = getDerived().parseType();
3699 if (Ty == nullptr)
3700 return nullptr;
3701 if (Quals != QualNone)
3702 Ty = make<QualType>(Ty, Quals);
3703 return Ty;
3704}
3705
3706// <type> ::= <builtin-type>
3707// ::= <qualified-type>
3708// ::= <function-type>
3709// ::= <class-enum-type>
3710// ::= <array-type>
3711// ::= <pointer-to-member-type>
3712// ::= <template-param>
3713// ::= <template-template-param> <template-args>
3714// ::= <decltype>
3715// ::= P <type> # pointer
3716// ::= R <type> # l-value reference
3717// ::= O <type> # r-value reference (C++11)
3718// ::= C <type> # complex pair (C99)
3719// ::= G <type> # imaginary (C99)
3720// ::= <substitution> # See Compression below
3721// extension ::= U <objc-name> <objc-type> # objc-type<identifier>
3722// extension ::= <vector-type> # <vector-type> starts with Dv
3723//
3724// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1
3725// <objc-type> ::= <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name>
3726template <typename Derived, typename Alloc>
3727Node *AbstractManglingParser<Derived, Alloc>::parseType() {
3728 Node *Result = nullptr;
3729
3730 switch (look()) {
3731 // ::= <qualified-type>
3732 case 'r':
3733 case 'V':
3734 case 'K': {
3735 unsigned AfterQuals = 0;
3736 if (look(AfterQuals) == 'r') ++AfterQuals;
3737 if (look(AfterQuals) == 'V') ++AfterQuals;
3738 if (look(AfterQuals) == 'K') ++AfterQuals;
3739
3740 if (look(AfterQuals) == 'F' ||
3741 (look(AfterQuals) == 'D' &&
3742 (look(AfterQuals + 1) == 'o' || look(AfterQuals + 1) == 'O' ||
3743 look(AfterQuals + 1) == 'w' || look(AfterQuals + 1) == 'x'))) {
3744 Result = getDerived().parseFunctionType();
3745 break;
3746 }
3747 DEMANGLE_FALLTHROUGH;
3748 }
3749 case 'U': {
3750 Result = getDerived().parseQualifiedType();
3751 break;
3752 }
3753 // <builtin-type> ::= v # void
3754 case 'v':
3755 ++First;
3756 return make<NameType>("void");
3757 // ::= w # wchar_t
3758 case 'w':
3759 ++First;
3760 return make<NameType>("wchar_t");
3761 // ::= b # bool
3762 case 'b':
3763 ++First;
3764 return make<NameType>("bool");
3765 // ::= c # char
3766 case 'c':
3767 ++First;
3768 return make<NameType>("char");
3769 // ::= a # signed char
3770 case 'a':
3771 ++First;
3772 return make<NameType>("signed char");
3773 // ::= h # unsigned char
3774 case 'h':
3775 ++First;
3776 return make<NameType>("unsigned char");
3777 // ::= s # short
3778 case 's':
3779 ++First;
3780 return make<NameType>("short");
3781 // ::= t # unsigned short
3782 case 't':
3783 ++First;
3784 return make<NameType>("unsigned short");
3785 // ::= i # int
3786 case 'i':
3787 ++First;
3788 return make<NameType>("int");
3789 // ::= j # unsigned int
3790 case 'j':
3791 ++First;
3792 return make<NameType>("unsigned int");
3793 // ::= l # long
3794 case 'l':
3795 ++First;
3796 return make<NameType>("long");
3797 // ::= m # unsigned long
3798 case 'm':
3799 ++First;
3800 return make<NameType>("unsigned long");
3801 // ::= x # long long, __int64
3802 case 'x':
3803 ++First;
3804 return make<NameType>("long long");
3805 // ::= y # unsigned long long, __int64
3806 case 'y':
3807 ++First;
3808 return make<NameType>("unsigned long long");
3809 // ::= n # __int128
3810 case 'n':
3811 ++First;
3812 return make<NameType>("__int128");
3813 // ::= o # unsigned __int128
3814 case 'o':
3815 ++First;
3816 return make<NameType>("unsigned __int128");
3817 // ::= f # float
3818 case 'f':
3819 ++First;
3820 return make<NameType>("float");
3821 // ::= d # double
3822 case 'd':
3823 ++First;
3824 return make<NameType>("double");
3825 // ::= e # long double, __float80
3826 case 'e':
3827 ++First;
3828 return make<NameType>("long double");
3829 // ::= g # __float128
3830 case 'g':
3831 ++First;
3832 return make<NameType>("__float128");
3833 // ::= z # ellipsis
3834 case 'z':
3835 ++First;
3836 return make<NameType>("...");
3837
3838 // <builtin-type> ::= u <source-name> # vendor extended type
3839 case 'u': {
3840 ++First;
3841 StringView Res = parseBareSourceName();
3842 if (Res.empty())
3843 return nullptr;
3844 // Typically, <builtin-type>s are not considered substitution candidates,
3845 // but the exception to that exception is vendor extended types (Itanium C++
3846 // ABI 5.9.1).
3847 Result = make<NameType>(Res);
3848 break;
3849 }
3850 case 'D':
3851 switch (look(1)) {
3852 // ::= Dd # IEEE 754r decimal floating point (64 bits)
3853 case 'd':
3854 First += 2;
3855 return make<NameType>("decimal64");
3856 // ::= De # IEEE 754r decimal floating point (128 bits)
3857 case 'e':
3858 First += 2;
3859 return make<NameType>("decimal128");
3860 // ::= Df # IEEE 754r decimal floating point (32 bits)
3861 case 'f':
3862 First += 2;
3863 return make<NameType>("decimal32");
3864 // ::= Dh # IEEE 754r half-precision floating point (16 bits)
3865 case 'h':
3866 First += 2;
3867 return make<NameType>("decimal16");
3868 // ::= Di # char32_t
3869 case 'i':
3870 First += 2;
3871 return make<NameType>("char32_t");
3872 // ::= Ds # char16_t
3873 case 's':
3874 First += 2;
3875 return make<NameType>("char16_t");
3876 // ::= Du # char8_t (C++2a, not yet in the Itanium spec)
3877 case 'u':
3878 First += 2;
3879 return make<NameType>("char8_t");
3880 // ::= Da # auto (in dependent new-expressions)
3881 case 'a':
3882 First += 2;
3883 return make<NameType>("auto");
3884 // ::= Dc # decltype(auto)
3885 case 'c':
3886 First += 2;
3887 return make<NameType>("decltype(auto)");
3888 // ::= Dn # std::nullptr_t (i.e., decltype(nullptr))
3889 case 'n':
3890 First += 2;
3891 return make<NameType>("std::nullptr_t");
3892
3893 // ::= <decltype>
3894 case 't':
3895 case 'T': {
3896 Result = getDerived().parseDecltype();
3897 break;
3898 }
3899 // extension ::= <vector-type> # <vector-type> starts with Dv
3900 case 'v': {
3901 Result = getDerived().parseVectorType();
3902 break;
3903 }
3904 // ::= Dp <type> # pack expansion (C++0x)
3905 case 'p': {
3906 First += 2;
3907 Node *Child = getDerived().parseType();
3908 if (!Child)
3909 return nullptr;
3910 Result = make<ParameterPackExpansion>(Child);
3911 break;
3912 }
3913 // Exception specifier on a function type.
3914 case 'o':
3915 case 'O':
3916 case 'w':
3917 // Transaction safe function type.
3918 case 'x':
3919 Result = getDerived().parseFunctionType();
3920 break;
3921 }
3922 break;
3923 // ::= <function-type>
3924 case 'F': {
3925 Result = getDerived().parseFunctionType();
3926 break;
3927 }
3928 // ::= <array-type>
3929 case 'A': {
3930 Result = getDerived().parseArrayType();
3931 break;
3932 }
3933 // ::= <pointer-to-member-type>
3934 case 'M': {
3935 Result = getDerived().parsePointerToMemberType();
3936 break;
3937 }
3938 // ::= <template-param>
3939 case 'T': {
3940 // This could be an elaborate type specifier on a <class-enum-type>.
3941 if (look(1) == 's' || look(1) == 'u' || look(1) == 'e') {
3942 Result = getDerived().parseClassEnumType();
3943 break;
3944 }
3945
3946 Result = getDerived().parseTemplateParam();
3947 if (Result == nullptr)
3948 return nullptr;
3949
3950 // Result could be either of:
3951 // <type> ::= <template-param>
3952 // <type> ::= <template-template-param> <template-args>
3953 //
3954 // <template-template-param> ::= <template-param>
3955 // ::= <substitution>
3956 //
3957 // If this is followed by some <template-args>, and we're permitted to
3958 // parse them, take the second production.
3959
3960 if (TryToParseTemplateArgs && look() == 'I') {
3961 Node *TA = getDerived().parseTemplateArgs();
3962 if (TA == nullptr)
3963 return nullptr;
3964 Result = make<NameWithTemplateArgs>(Result, TA);
3965 }
3966 break;
3967 }
3968 // ::= P <type> # pointer
3969 case 'P': {
3970 ++First;
3971 Node *Ptr = getDerived().parseType();
3972 if (Ptr == nullptr)
3973 return nullptr;
3974 Result = make<PointerType>(Ptr);
3975 break;
3976 }
3977 // ::= R <type> # l-value reference
3978 case 'R': {
3979 ++First;
3980 Node *Ref = getDerived().parseType();
3981 if (Ref == nullptr)
3982 return nullptr;
3983 Result = make<ReferenceType>(Ref, ReferenceKind::LValue);
3984 break;
3985 }
3986 // ::= O <type> # r-value reference (C++11)
3987 case 'O': {
3988 ++First;
3989 Node *Ref = getDerived().parseType();
3990 if (Ref == nullptr)
3991 return nullptr;
3992 Result = make<ReferenceType>(Ref, ReferenceKind::RValue);
3993 break;
3994 }
3995 // ::= C <type> # complex pair (C99)
3996 case 'C': {
3997 ++First;
3998 Node *P = getDerived().parseType();
3999 if (P == nullptr)
4000 return nullptr;
4001 Result = make<PostfixQualifiedType>(P, " complex");
4002 break;
4003 }
4004 // ::= G <type> # imaginary (C99)
4005 case 'G': {
4006 ++First;
4007 Node *P = getDerived().parseType();
4008 if (P == nullptr)
4009 return P;
4010 Result = make<PostfixQualifiedType>(P, " imaginary");
4011 break;
4012 }
4013 // ::= <substitution> # See Compression below
4014 case 'S': {
4015 if (look(1) && look(1) != 't') {
4016 Node *Sub = getDerived().parseSubstitution();
4017 if (Sub == nullptr)
4018 return nullptr;
4019
4020 // Sub could be either of:
4021 // <type> ::= <substitution>
4022 // <type> ::= <template-template-param> <template-args>
4023 //
4024 // <template-template-param> ::= <template-param>
4025 // ::= <substitution>
4026 //
4027 // If this is followed by some <template-args>, and we're permitted to
4028 // parse them, take the second production.
4029
4030 if (TryToParseTemplateArgs && look() == 'I') {
4031 Node *TA = getDerived().parseTemplateArgs();
4032 if (TA == nullptr)
4033 return nullptr;
4034 Result = make<NameWithTemplateArgs>(Sub, TA);
4035 break;
4036 }
4037
4038 // If all we parsed was a substitution, don't re-insert into the
4039 // substitution table.
4040 return Sub;
4041 }
4042 DEMANGLE_FALLTHROUGH;
4043 }
4044 // ::= <class-enum-type>
4045 default: {
4046 Result = getDerived().parseClassEnumType();
4047 break;
4048 }
4049 }
4050
4051 // If we parsed a type, insert it into the substitution table. Note that all
4052 // <builtin-type>s and <substitution>s have already bailed out, because they
4053 // don't get substitutions.
4054 if (Result != nullptr)
4055 Subs.push_back(Result);
4056 return Result;
4057}
4058
4059template <typename Derived, typename Alloc>
4060Node *AbstractManglingParser<Derived, Alloc>::parsePrefixExpr(StringView Kind) {
4061 Node *E = getDerived().parseExpr();
4062 if (E == nullptr)
4063 return nullptr;
4064 return make<PrefixExpr>(Kind, E);
4065}
4066
4067template <typename Derived, typename Alloc>
4068Node *AbstractManglingParser<Derived, Alloc>::parseBinaryExpr(StringView Kind) {
4069 Node *LHS = getDerived().parseExpr();
4070 if (LHS == nullptr)
4071 return nullptr;
4072 Node *RHS = getDerived().parseExpr();
4073 if (RHS == nullptr)
4074 return nullptr;
4075 return make<BinaryExpr>(LHS, Kind, RHS);
4076}
4077
4078template <typename Derived, typename Alloc>
4079Node *
4080AbstractManglingParser<Derived, Alloc>::parseIntegerLiteral(StringView Lit) {
4081 StringView Tmp = parseNumber(true);
4082 if (!Tmp.empty() && consumeIf('E'))
4083 return make<IntegerLiteral>(Lit, Tmp);
4084 return nullptr;
4085}
4086
4087// <CV-Qualifiers> ::= [r] [V] [K]
4088template <typename Alloc, typename Derived>
4089Qualifiers AbstractManglingParser<Alloc, Derived>::parseCVQualifiers() {
4090 Qualifiers CVR = QualNone;
4091 if (consumeIf('r'))
4092 CVR |= QualRestrict;
4093 if (consumeIf('V'))
4094 CVR |= QualVolatile;
4095 if (consumeIf('K'))
4096 CVR |= QualConst;
4097 return CVR;
4098}
4099
4100// <function-param> ::= fp <top-level CV-Qualifiers> _ # L == 0, first parameter
4101// ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters
4102// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter
4103// ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters
4104template <typename Derived, typename Alloc>
4105Node *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() {
4106 if (consumeIf("fp")) {
4107 parseCVQualifiers();
4108 StringView Num = parseNumber();
4109 if (!consumeIf('_'))
4110 return nullptr;
4111 return make<FunctionParam>(Num);
4112 }
4113 if (consumeIf("fL")) {
4114 if (parseNumber().empty())
4115 return nullptr;
4116 if (!consumeIf('p'))
4117 return nullptr;
4118 parseCVQualifiers();
4119 StringView Num = parseNumber();
4120 if (!consumeIf('_'))
4121 return nullptr;
4122 return make<FunctionParam>(Num);
4123 }
4124 return nullptr;
4125}
4126
4127// [gs] nw <expression>* _ <type> E # new (expr-list) type
4128// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
4129// [gs] na <expression>* _ <type> E # new[] (expr-list) type
4130// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
4131// <initializer> ::= pi <expression>* E # parenthesized initialization
4132template <typename Derived, typename Alloc>
4133Node *AbstractManglingParser<Derived, Alloc>::parseNewExpr() {
4134 bool Global = consumeIf("gs");
4135 bool IsArray = look(1) == 'a';
4136 if (!consumeIf("nw") && !consumeIf("na"))
4137 return nullptr;
4138 size_t Exprs = Names.size();
4139 while (!consumeIf('_')) {
4140 Node *Ex = getDerived().parseExpr();
4141 if (Ex == nullptr)
4142 return nullptr;
4143 Names.push_back(Ex);
4144 }
4145 NodeArray ExprList = popTrailingNodeArray(Exprs);
4146 Node *Ty = getDerived().parseType();
4147 if (Ty == nullptr)
4148 return Ty;
4149 if (consumeIf("pi")) {
4150 size_t InitsBegin = Names.size();
4151 while (!consumeIf('E')) {
4152 Node *Init = getDerived().parseExpr();
4153 if (Init == nullptr)
4154 return Init;
4155 Names.push_back(Init);
4156 }
4157 NodeArray Inits = popTrailingNodeArray(InitsBegin);
4158 return make<NewExpr>(ExprList, Ty, Inits, Global, IsArray);
4159 } else if (!consumeIf('E'))
4160 return nullptr;
4161 return make<NewExpr>(ExprList, Ty, NodeArray(), Global, IsArray);
4162}
4163
4164// cv <type> <expression> # conversion with one argument
4165// cv <type> _ <expression>* E # conversion with a different number of arguments
4166template <typename Derived, typename Alloc>
4167Node *AbstractManglingParser<Derived, Alloc>::parseConversionExpr() {
4168 if (!consumeIf("cv"))
4169 return nullptr;
4170 Node *Ty;
4171 {
4172 SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false);
4173 Ty = getDerived().parseType();
4174 }
4175
4176 if (Ty == nullptr)
4177 return nullptr;
4178
4179 if (consumeIf('_')) {
4180 size_t ExprsBegin = Names.size();
4181 while (!consumeIf('E')) {
4182 Node *E = getDerived().parseExpr();
4183 if (E == nullptr)
4184 return E;
4185 Names.push_back(E);
4186 }
4187 NodeArray Exprs = popTrailingNodeArray(ExprsBegin);
4188 return make<ConversionExpr>(Ty, Exprs);
4189 }
4190
4191 Node *E[1] = {getDerived().parseExpr()};
4192 if (E[0] == nullptr)
4193 return nullptr;
4194 return make<ConversionExpr>(Ty, makeNodeArray(E, E + 1));
4195}
4196
4197// <expr-primary> ::= L <type> <value number> E # integer literal
4198// ::= L <type> <value float> E # floating literal
4199// ::= L <string type> E # string literal
4200// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE")
4201// ::= L <lambda type> E # lambda expression
4202// FIXME: ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000)
4203// ::= L <mangled-name> E # external name
4204template <typename Derived, typename Alloc>
4205Node *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() {
4206 if (!consumeIf('L'))
4207 return nullptr;
4208 switch (look()) {
4209 case 'w':
4210 ++First;
4211 return getDerived().parseIntegerLiteral("wchar_t");
4212 case 'b':
4213 if (consumeIf("b0E"))
4214 return make<BoolExpr>(0);
4215 if (consumeIf("b1E"))
4216 return make<BoolExpr>(1);
4217 return nullptr;
4218 case 'c':
4219 ++First;
4220 return getDerived().parseIntegerLiteral("char");
4221 case 'a':
4222 ++First;
4223 return getDerived().parseIntegerLiteral("signed char");
4224 case 'h':
4225 ++First;
4226 return getDerived().parseIntegerLiteral("unsigned char");
4227 case 's':
4228 ++First;
4229 return getDerived().parseIntegerLiteral("short");
4230 case 't':
4231 ++First;
4232 return getDerived().parseIntegerLiteral("unsigned short");
4233 case 'i':
4234 ++First;
4235 return getDerived().parseIntegerLiteral("");
4236 case 'j':
4237 ++First;
4238 return getDerived().parseIntegerLiteral("u");
4239 case 'l':
4240 ++First;
4241 return getDerived().parseIntegerLiteral("l");
4242 case 'm':
4243 ++First;
4244 return getDerived().parseIntegerLiteral("ul");
4245 case 'x':
4246 ++First;
4247 return getDerived().parseIntegerLiteral("ll");
4248 case 'y':
4249 ++First;
4250 return getDerived().parseIntegerLiteral("ull");
4251 case 'n':
4252 ++First;
4253 return getDerived().parseIntegerLiteral("__int128");
4254 case 'o':
4255 ++First;
4256 return getDerived().parseIntegerLiteral("unsigned __int128");
4257 case 'f':
4258 ++First;
4259 return getDerived().template parseFloatingLiteral<float>();
4260 case 'd':
4261 ++First;
4262 return getDerived().template parseFloatingLiteral<double>();
4263 case 'e':
4264 ++First;
4265 return getDerived().template parseFloatingLiteral<long double>();
4266 case '_':
4267 if (consumeIf("_Z")) {
4268 Node *R = getDerived().parseEncoding();
4269 if (R != nullptr && consumeIf('E'))
4270 return R;
4271 }
4272 return nullptr;
4273 case 'A': {
4274 Node *T = getDerived().parseType();
4275 if (T == nullptr)
4276 return nullptr;
4277 // FIXME: We need to include the string contents in the mangling.
4278 if (consumeIf('E'))
4279 return make<StringLiteral>(T);
4280 return nullptr;
4281 }
4282 case 'D':
4283 if (consumeIf("DnE"))
4284 return make<NameType>("nullptr");
4285 return nullptr;
4286 case 'T':
4287 // Invalid mangled name per
4288 // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
4289 return nullptr;
4290 case 'U': {
4291 // FIXME: Should we support LUb... for block literals?
4292 if (look(1) != 'l')
4293 return nullptr;
4294 Node *T = parseUnnamedTypeName(nullptr);
4295 if (!T || !consumeIf('E'))
4296 return nullptr;
4297 return make<LambdaExpr>(T);
4298 }
4299 default: {
4300 // might be named type
4301 Node *T = getDerived().parseType();
4302 if (T == nullptr)
4303 return nullptr;
4304 StringView N = parseNumber();
4305 if (N.empty())
4306 return nullptr;
4307 if (!consumeIf('E'))
4308 return nullptr;
4309 return make<IntegerCastExpr>(T, N);
4310 }
4311 }
4312}
4313
4314// <braced-expression> ::= <expression>
4315// ::= di <field source-name> <braced-expression> # .name = expr
4316// ::= dx <index expression> <braced-expression> # [expr] = expr
4317// ::= dX <range begin expression> <range end expression> <braced-expression>
4318template <typename Derived, typename Alloc>
4319Node *AbstractManglingParser<Derived, Alloc>::parseBracedExpr() {
4320 if (look() == 'd') {
4321 switch (look(1)) {
4322 case 'i': {
4323 First += 2;
4324 Node *Field = getDerived().parseSourceName(/*NameState=*/nullptr);
4325 if (Field == nullptr)
4326 return nullptr;
4327 Node *Init = getDerived().parseBracedExpr();
4328 if (Init == nullptr)
4329 return nullptr;
4330 return make<BracedExpr>(Field, Init, /*isArray=*/false);
4331 }
4332 case 'x': {
4333 First += 2;
4334 Node *Index = getDerived().parseExpr();
4335 if (Index == nullptr)
4336 return nullptr;
4337 Node *Init = getDerived().parseBracedExpr();
4338 if (Init == nullptr)
4339 return nullptr;
4340 return make<BracedExpr>(Index, Init, /*isArray=*/true);
4341 }
4342 case 'X': {
4343 First += 2;
4344 Node *RangeBegin = getDerived().parseExpr();
4345 if (RangeBegin == nullptr)
4346 return nullptr;
4347 Node *RangeEnd = getDerived().parseExpr();
4348 if (RangeEnd == nullptr)
4349 return nullptr;
4350 Node *Init = getDerived().parseBracedExpr();
4351 if (Init == nullptr)
4352 return nullptr;
4353 return make<BracedRangeExpr>(RangeBegin, RangeEnd, Init);
4354 }
4355 }
4356 }
4357 return getDerived().parseExpr();
4358}
4359
4360// (not yet in the spec)
4361// <fold-expr> ::= fL <binary-operator-name> <expression> <expression>
4362// ::= fR <binary-operator-name> <expression> <expression>
4363// ::= fl <binary-operator-name> <expression>
4364// ::= fr <binary-operator-name> <expression>
4365template <typename Derived, typename Alloc>
4366Node *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() {
4367 if (!consumeIf('f'))
4368 return nullptr;
4369
4370 char FoldKind = look();
4371 bool IsLeftFold, HasInitializer;
4372 HasInitializer = FoldKind == 'L' || FoldKind == 'R';
4373 if (FoldKind == 'l' || FoldKind == 'L')
4374 IsLeftFold = true;
4375 else if (FoldKind == 'r' || FoldKind == 'R')
4376 IsLeftFold = false;
4377 else
4378 return nullptr;
4379 ++First;
4380
4381 // FIXME: This map is duplicated in parseOperatorName and parseExpr.
4382 StringView OperatorName;
4383 if (consumeIf("aa")) OperatorName = "&&";
4384 else if (consumeIf("an")) OperatorName = "&";
4385 else if (consumeIf("aN")) OperatorName = "&=";
4386 else if (consumeIf("aS")) OperatorName = "=";
4387 else if (consumeIf("cm")) OperatorName = ",";
4388 else if (consumeIf("ds")) OperatorName = ".*";
4389 else if (consumeIf("dv")) OperatorName = "/";
4390 else if (consumeIf("dV")) OperatorName = "/=";
4391 else if (consumeIf("eo")) OperatorName = "^";
4392 else if (consumeIf("eO")) OperatorName = "^=";
4393 else if (consumeIf("eq")) OperatorName = "==";
4394 else if (consumeIf("ge")) OperatorName = ">=";
4395 else if (consumeIf("gt")) OperatorName = ">";
4396 else if (consumeIf("le")) OperatorName = "<=";
4397 else if (consumeIf("ls")) OperatorName = "<<";
4398 else if (consumeIf("lS")) OperatorName = "<<=";
4399 else if (consumeIf("lt")) OperatorName = "<";
4400 else if (consumeIf("mi")) OperatorName = "-";
4401 else if (consumeIf("mI")) OperatorName = "-=";
4402 else if (consumeIf("ml")) OperatorName = "*";
4403 else if (consumeIf("mL")) OperatorName = "*=";
4404 else if (consumeIf("ne")) OperatorName = "!=";
4405 else if (consumeIf("oo")) OperatorName = "||";
4406 else if (consumeIf("or")) OperatorName = "|";
4407 else if (consumeIf("oR")) OperatorName = "|=";
4408 else if (consumeIf("pl")) OperatorName = "+";
4409 else if (consumeIf("pL")) OperatorName = "+=";
4410 else if (consumeIf("rm")) OperatorName = "%";
4411 else if (consumeIf("rM")) OperatorName = "%=";
4412 else if (consumeIf("rs")) OperatorName = ">>";
4413 else if (consumeIf("rS")) OperatorName = ">>=";
4414 else return nullptr;
4415
4416 Node *Pack = getDerived().parseExpr(), *Init = nullptr;
4417 if (Pack == nullptr)
4418 return nullptr;
4419 if (HasInitializer) {
4420 Init = getDerived().parseExpr();
4421 if (Init == nullptr)
4422 return nullptr;
4423 }
4424
4425 if (IsLeftFold && Init)
4426 std::swap(Pack, Init);
4427
4428 return make<FoldExpr>(IsLeftFold, OperatorName, Pack, Init);
4429}
4430
4431// <expression> ::= <unary operator-name> <expression>
4432// ::= <binary operator-name> <expression> <expression>
4433// ::= <ternary operator-name> <expression> <expression> <expression>
4434// ::= cl <expression>+ E # call
4435// ::= cv <type> <expression> # conversion with one argument
4436// ::= cv <type> _ <expression>* E # conversion with a different number of arguments
4437// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type
4438// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init)
4439// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type
4440// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init)
4441// ::= [gs] dl <expression> # delete expression
4442// ::= [gs] da <expression> # delete[] expression
4443// ::= pp_ <expression> # prefix ++
4444// ::= mm_ <expression> # prefix --
4445// ::= ti <type> # typeid (type)
4446// ::= te <expression> # typeid (expression)
4447// ::= dc <type> <expression> # dynamic_cast<type> (expression)
4448// ::= sc <type> <expression> # static_cast<type> (expression)
4449// ::= cc <type> <expression> # const_cast<type> (expression)
4450// ::= rc <type> <expression> # reinterpret_cast<type> (expression)
4451// ::= st <type> # sizeof (a type)
4452// ::= sz <expression> # sizeof (an expression)
4453// ::= at <type> # alignof (a type)
4454// ::= az <expression> # alignof (an expression)
4455// ::= nx <expression> # noexcept (expression)
4456// ::= <template-param>
4457// ::= <function-param>
4458// ::= dt <expression> <unresolved-name> # expr.name
4459// ::= pt <expression> <unresolved-name> # expr->name
4460// ::= ds <expression> <expression> # expr.*expr
4461// ::= sZ <template-param> # size of a parameter pack
4462// ::= sZ <function-param> # size of a function parameter pack
4463// ::= sP <template-arg>* E # sizeof...(T), size of a captured template parameter pack from an alias template
4464// ::= sp <expression> # pack expansion
4465// ::= tw <expression> # throw expression
4466// ::= tr # throw with no operand (rethrow)
4467// ::= <unresolved-name> # f(p), N::f(p), ::f(p),
4468// # freestanding dependent name (e.g., T::x),
4469// # objectless nonstatic member reference
4470// ::= fL <binary-operator-name> <expression> <expression>
4471// ::= fR <binary-operator-name> <expression> <expression>
4472// ::= fl <binary-operator-name> <expression>
4473// ::= fr <binary-operator-name> <expression>
4474// ::= <expr-primary>
4475template <typename Derived, typename Alloc>
4476Node *AbstractManglingParser<Derived, Alloc>::parseExpr() {
4477 bool Global = consumeIf("gs");
4478 if (numLeft() < 2)
4479 return nullptr;
4480
4481 switch (*First) {
4482 case 'L':
4483 return getDerived().parseExprPrimary();
4484 case 'T':
4485 return getDerived().parseTemplateParam();
4486 case 'f': {
4487 // Disambiguate a fold expression from a <function-param>.
4488 if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2))))
4489 return getDerived().parseFunctionParam();
4490 return getDerived().parseFoldExpr();
4491 }
4492 case 'a':
4493 switch (First[1]) {
4494 case 'a':
4495 First += 2;
4496 return getDerived().parseBinaryExpr("&&");
4497 case 'd':
4498 First += 2;
4499 return getDerived().parsePrefixExpr("&");
4500 case 'n':
4501 First += 2;
4502 return getDerived().parseBinaryExpr("&");
4503 case 'N':
4504 First += 2;
4505 return getDerived().parseBinaryExpr("&=");
4506 case 'S':
4507 First += 2;
4508 return getDerived().parseBinaryExpr("=");
4509 case 't': {
4510 First += 2;
4511 Node *Ty = getDerived().parseType();
4512 if (Ty == nullptr)
4513 return nullptr;
4514 return make<EnclosingExpr>("alignof (", Ty, ")");
4515 }
4516 case 'z': {
4517 First += 2;
4518 Node *Ty = getDerived().parseExpr();
4519 if (Ty == nullptr)
4520 return nullptr;
4521 return make<EnclosingExpr>("alignof (", Ty, ")");
4522 }
4523 }
4524 return nullptr;
4525 case 'c':
4526 switch (First[1]) {
4527 // cc <type> <expression> # const_cast<type>(expression)
4528 case 'c': {
4529 First += 2;
4530 Node *Ty = getDerived().parseType();
4531 if (Ty == nullptr)
4532 return Ty;
4533 Node *Ex = getDerived().parseExpr();
4534 if (Ex == nullptr)
4535 return Ex;
4536 return make<CastExpr>("const_cast", Ty, Ex);
4537 }
4538 // cl <expression>+ E # call
4539 case 'l': {
4540 First += 2;
4541 Node *Callee = getDerived().parseExpr();
4542 if (Callee == nullptr)
4543 return Callee;
4544 size_t ExprsBegin = Names.size();
4545 while (!consumeIf('E')) {
4546 Node *E = getDerived().parseExpr();
4547 if (E == nullptr)
4548 return E;
4549 Names.push_back(E);
4550 }
4551 return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin));
4552 }
4553 case 'm':
4554 First += 2;
4555 return getDerived().parseBinaryExpr(",");
4556 case 'o':
4557 First += 2;
4558 return getDerived().parsePrefixExpr("~");
4559 case 'v':
4560 return getDerived().parseConversionExpr();
4561 }
4562 return nullptr;
4563 case 'd':
4564 switch (First[1]) {
4565 case 'a': {
4566 First += 2;
4567 Node *Ex = getDerived().parseExpr();
4568 if (Ex == nullptr)
4569 return Ex;
4570 return make<DeleteExpr>(Ex, Global, /*is_array=*/true);
4571 }
4572 case 'c': {
4573 First += 2;
4574 Node *T = getDerived().parseType();
4575 if (T == nullptr)
4576 return T;
4577 Node *Ex = getDerived().parseExpr();
4578 if (Ex == nullptr)
4579 return Ex;
4580 return make<CastExpr>("dynamic_cast", T, Ex);
4581 }
4582 case 'e':
4583 First += 2;
4584 return getDerived().parsePrefixExpr("*");
4585 case 'l': {
4586 First += 2;
4587 Node *E = getDerived().parseExpr();
4588 if (E == nullptr)
4589 return E;
4590 return make<DeleteExpr>(E, Global, /*is_array=*/false);
4591 }
4592 case 'n':
4593 return getDerived().parseUnresolvedName();
4594 case 's': {
4595 First += 2;
4596 Node *LHS = getDerived().parseExpr();
4597 if (LHS == nullptr)
4598 return nullptr;
4599 Node *RHS = getDerived().parseExpr();
4600 if (RHS == nullptr)
4601 return nullptr;
4602 return make<MemberExpr>(LHS, ".*", RHS);
4603 }
4604 case 't': {
4605 First += 2;
4606 Node *LHS = getDerived().parseExpr();
4607 if (LHS == nullptr)
4608 return LHS;
4609 Node *RHS = getDerived().parseExpr();
4610 if (RHS == nullptr)
4611 return nullptr;
4612 return make<MemberExpr>(LHS, ".", RHS);
4613 }
4614 case 'v':
4615 First += 2;
4616 return getDerived().parseBinaryExpr("/");
4617 case 'V':
4618 First += 2;
4619 return getDerived().parseBinaryExpr("/=");
4620 }
4621 return nullptr;
4622 case 'e':
4623 switch (First[1]) {
4624 case 'o':
4625 First += 2;
4626 return getDerived().parseBinaryExpr("^");
4627 case 'O':
4628 First += 2;
4629 return getDerived().parseBinaryExpr("^=");
4630 case 'q':
4631 First += 2;
4632 return getDerived().parseBinaryExpr("==");
4633 }
4634 return nullptr;
4635 case 'g':
4636 switch (First[1]) {
4637 case 'e':
4638 First += 2;
4639 return getDerived().parseBinaryExpr(">=");
4640 case 't':
4641 First += 2;
4642 return getDerived().parseBinaryExpr(">");
4643 }
4644 return nullptr;
4645 case 'i':
4646 switch (First[1]) {
4647 case 'x': {
4648 First += 2;
4649 Node *Base = getDerived().parseExpr();
4650 if (Base == nullptr)
4651 return nullptr;
4652 Node *Index = getDerived().parseExpr();
4653 if (Index == nullptr)
4654 return Index;
4655 return make<ArraySubscriptExpr>(Base, Index);
4656 }
4657 case 'l': {
4658 First += 2;
4659 size_t InitsBegin = Names.size();
4660 while (!consumeIf('E')) {
4661 Node *E = getDerived().parseBracedExpr();
4662 if (E == nullptr)
4663 return nullptr;
4664 Names.push_back(E);
4665 }
4666 return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin));
4667 }
4668 }
4669 return nullptr;
4670 case 'l':
4671 switch (First[1]) {
4672 case 'e':
4673 First += 2;
4674 return getDerived().parseBinaryExpr("<=");
4675 case 's':
4676 First += 2;
4677 return getDerived().parseBinaryExpr("<<");
4678 case 'S':
4679 First += 2;
4680 return getDerived().parseBinaryExpr("<<=");
4681 case 't':
4682 First += 2;
4683 return getDerived().parseBinaryExpr("<");
4684 }
4685 return nullptr;
4686 case 'm':
4687 switch (First[1]) {
4688 case 'i':
4689 First += 2;
4690 return getDerived().parseBinaryExpr("-");
4691 case 'I':
4692 First += 2;
4693 return getDerived().parseBinaryExpr("-=");
4694 case 'l':
4695 First += 2;
4696 return getDerived().parseBinaryExpr("*");
4697 case 'L':
4698 First += 2;
4699 return getDerived().parseBinaryExpr("*=");
4700 case 'm':
4701 First += 2;
4702 if (consumeIf('_'))
4703 return getDerived().parsePrefixExpr("--");
4704 Node *Ex = getDerived().parseExpr();
4705 if (Ex == nullptr)
4706 return nullptr;
4707 return make<PostfixExpr>(Ex, "--");
4708 }
4709 return nullptr;
4710 case 'n':
4711 switch (First[1]) {
4712 case 'a':
4713 case 'w':
4714 return getDerived().parseNewExpr();
4715 case 'e':
4716 First += 2;
4717 return getDerived().parseBinaryExpr("!=");
4718 case 'g':
4719 First += 2;
4720 return getDerived().parsePrefixExpr("-");
4721 case 't':
4722 First += 2;
4723 return getDerived().parsePrefixExpr("!");
4724 case 'x':
4725 First += 2;
4726 Node *Ex = getDerived().parseExpr();
4727 if (Ex == nullptr)
4728 return Ex;
4729 return make<EnclosingExpr>("noexcept (", Ex, ")");
4730 }
4731 return nullptr;
4732 case 'o':
4733 switch (First[1]) {
4734 case 'n':
4735 return getDerived().parseUnresolvedName();
4736 case 'o':
4737 First += 2;
4738 return getDerived().parseBinaryExpr("||");
4739 case 'r':
4740 First += 2;
4741 return getDerived().parseBinaryExpr("|");
4742 case 'R':
4743 First += 2;
4744 return getDerived().parseBinaryExpr("|=");
4745 }
4746 return nullptr;
4747 case 'p':
4748 switch (First[1]) {
4749 case 'm':
4750 First += 2;
4751 return getDerived().parseBinaryExpr("->*");
4752 case 'l':
4753 First += 2;
4754 return getDerived().parseBinaryExpr("+");
4755 case 'L':
4756 First += 2;
4757 return getDerived().parseBinaryExpr("+=");
4758 case 'p': {
4759 First += 2;
4760 if (consumeIf('_'))
4761 return getDerived().parsePrefixExpr("++");
4762 Node *Ex = getDerived().parseExpr();
4763 if (Ex == nullptr)
4764 return Ex;
4765 return make<PostfixExpr>(Ex, "++");
4766 }
4767 case 's':
4768 First += 2;
4769 return getDerived().parsePrefixExpr("+");
4770 case 't': {
4771 First += 2;
4772 Node *L = getDerived().parseExpr();
4773 if (L == nullptr)
4774 return nullptr;
4775 Node *R = getDerived().parseExpr();
4776 if (R == nullptr)
4777 return nullptr;
4778 return make<MemberExpr>(L, "->", R);
4779 }
4780 }
4781 return nullptr;
4782 case 'q':
4783 if (First[1] == 'u') {
4784 First += 2;
4785 Node *Cond = getDerived().parseExpr();
4786 if (Cond == nullptr)
4787 return nullptr;
4788 Node *LHS = getDerived().parseExpr();
4789 if (LHS == nullptr)
4790 return nullptr;
4791 Node *RHS = getDerived().parseExpr();
4792 if (RHS == nullptr)
4793 return nullptr;
4794 return make<ConditionalExpr>(Cond, LHS, RHS);
4795 }
4796 return nullptr;
4797 case 'r':
4798 switch (First[1]) {
4799 case 'c': {
4800 First += 2;
4801 Node *T = getDerived().parseType();
4802 if (T == nullptr)
4803 return T;
4804 Node *Ex = getDerived().parseExpr();
4805 if (Ex == nullptr)
4806 return Ex;
4807 return make<CastExpr>("reinterpret_cast", T, Ex);
4808 }
4809 case 'm':
4810 First += 2;
4811 return getDerived().parseBinaryExpr("%");
4812 case 'M':
4813 First += 2;
4814 return getDerived().parseBinaryExpr("%=");
4815 case 's':
4816 First += 2;
4817 return getDerived().parseBinaryExpr(">>");
4818 case 'S':
4819 First += 2;
4820 return getDerived().parseBinaryExpr(">>=");
4821 }
4822 return nullptr;
4823 case 's':
4824 switch (First[1]) {
4825 case 'c': {
4826 First += 2;
4827 Node *T = getDerived().parseType();
4828 if (T == nullptr)
4829 return T;
4830 Node *Ex = getDerived().parseExpr();
4831 if (Ex == nullptr)
4832 return Ex;
4833 return make<CastExpr>("static_cast", T, Ex);
4834 }
4835 case 'p': {
4836 First += 2;
4837 Node *Child = getDerived().parseExpr();
4838 if (Child == nullptr)
4839 return nullptr;
4840 return make<ParameterPackExpansion>(Child);
4841 }
4842 case 'r':
4843 return getDerived().parseUnresolvedName();
4844 case 't': {
4845 First += 2;
4846 Node *Ty = getDerived().parseType();
4847 if (Ty == nullptr)
4848 return Ty;
4849 return make<EnclosingExpr>("sizeof (", Ty, ")");
4850 }
4851 case 'z': {
4852 First += 2;
4853 Node *Ex = getDerived().parseExpr();
4854 if (Ex == nullptr)
4855 return Ex;
4856 return make<EnclosingExpr>("sizeof (", Ex, ")");
4857 }
4858 case 'Z':
4859 First += 2;
4860 if (look() == 'T') {
4861 Node *R = getDerived().parseTemplateParam();
4862 if (R == nullptr)
4863 return nullptr;
4864 return make<SizeofParamPackExpr>(R);
4865 } else if (look() == 'f') {
4866 Node *FP = getDerived().parseFunctionParam();
4867 if (FP == nullptr)
4868 return nullptr;
4869 return make<EnclosingExpr>("sizeof... (", FP, ")");
4870 }
4871 return nullptr;
4872 case 'P': {
4873 First += 2;
4874 size_t ArgsBegin = Names.size();
4875 while (!consumeIf('E')) {
4876 Node *Arg = getDerived().parseTemplateArg();
4877 if (Arg == nullptr)
4878 return nullptr;
4879 Names.push_back(Arg);
4880 }
4881 auto *Pack = make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin));
4882 if (!Pack)
4883 return nullptr;
4884 return make<EnclosingExpr>("sizeof... (", Pack, ")");
4885 }
4886 }
4887 return nullptr;
4888 case 't':
4889 switch (First[1]) {
4890 case 'e': {
4891 First += 2;
4892 Node *Ex = getDerived().parseExpr();
4893 if (Ex == nullptr)
4894 return Ex;
4895 return make<EnclosingExpr>("typeid (", Ex, ")");
4896 }
4897 case 'i': {
4898 First += 2;
4899 Node *Ty = getDerived().parseType();
4900 if (Ty == nullptr)
4901 return Ty;
4902 return make<EnclosingExpr>("typeid (", Ty, ")");
4903 }
4904 case 'l': {
4905 First += 2;
4906 Node *Ty = getDerived().parseType();
4907 if (Ty == nullptr)
4908 return nullptr;
4909 size_t InitsBegin = Names.size();
4910 while (!consumeIf('E')) {
4911 Node *E = getDerived().parseBracedExpr();
4912 if (E == nullptr)
4913 return nullptr;
4914 Names.push_back(E);
4915 }
4916 return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin));
4917 }
4918 case 'r':
4919 First += 2;
4920 return make<NameType>("throw");
4921 case 'w': {
4922 First += 2;
4923 Node *Ex = getDerived().parseExpr();
4924 if (Ex == nullptr)
4925 return nullptr;
4926 return make<ThrowExpr>(Ex);
4927 }
4928 }
4929 return nullptr;
4930 case '1':
4931 case '2':
4932 case '3':
4933 case '4':
4934 case '5':
4935 case '6':
4936 case '7':
4937 case '8':
4938 case '9':
4939 return getDerived().parseUnresolvedName();
4940 }
4941
4942 if (consumeIf("u8__uuidoft")) {
4943 Node *Ty = getDerived().parseType();
4944 if (!Ty)
4945 return nullptr;
4946 return make<UUIDOfExpr>(Ty);
4947 }
4948
4949 if (consumeIf("u8__uuidofz")) {
4950 Node *Ex = getDerived().parseExpr();
4951 if (!Ex)
4952 return nullptr;
4953 return make<UUIDOfExpr>(Ex);
4954 }
4955
4956 return nullptr;
4957}
4958
4959// <call-offset> ::= h <nv-offset> _
4960// ::= v <v-offset> _
4961//
4962// <nv-offset> ::= <offset number>
4963// # non-virtual base override
4964//
4965// <v-offset> ::= <offset number> _ <virtual offset number>
4966// # virtual base override, with vcall offset
4967template <typename Alloc, typename Derived>
4968bool AbstractManglingParser<Alloc, Derived>::parseCallOffset() {
4969 // Just scan through the call offset, we never add this information into the
4970 // output.
4971 if (consumeIf('h'))
4972 return parseNumber(true).empty() || !consumeIf('_');
4973 if (consumeIf('v'))
4974 return parseNumber(true).empty() || !consumeIf('_') ||
4975 parseNumber(true).empty() || !consumeIf('_');
4976 return true;
4977}
4978
4979// <special-name> ::= TV <type> # virtual table
4980// ::= TT <type> # VTT structure (construction vtable index)
4981// ::= TI <type> # typeinfo structure
4982// ::= TS <type> # typeinfo name (null-terminated byte string)
4983// ::= Tc <call-offset> <call-offset> <base encoding>
4984// # base is the nominal target function of thunk
4985// # first call-offset is 'this' adjustment
4986// # second call-offset is result adjustment
4987// ::= T <call-offset> <base encoding>
4988// # base is the nominal target function of thunk
4989// ::= GV <object name> # Guard variable for one-time initialization
4990// # No <type>
4991// ::= TW <object name> # Thread-local wrapper
4992// ::= TH <object name> # Thread-local initialization
4993// ::= GR <object name> _ # First temporary
4994// ::= GR <object name> <seq-id> _ # Subsequent temporaries
4995// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
4996// extension ::= GR <object name> # reference temporary for object
4997template <typename Derived, typename Alloc>
4998Node *AbstractManglingParser<Derived, Alloc>::parseSpecialName() {
4999 switch (look()) {
5000 case 'T':
5001 switch (look(1)) {
5002 // TV <type> # virtual table
5003 case 'V': {
5004 First += 2;
5005 Node *Ty = getDerived().parseType();
5006 if (Ty == nullptr)
5007 return nullptr;
5008 return make<SpecialName>("vtable for ", Ty);
5009 }
5010 // TT <type> # VTT structure (construction vtable index)
5011 case 'T': {
5012 First += 2;
5013 Node *Ty = getDerived().parseType();
5014 if (Ty == nullptr)
5015 return nullptr;
5016 return make<SpecialName>("VTT for ", Ty);
5017 }
5018 // TI <type> # typeinfo structure
5019 case 'I': {
5020 First += 2;
5021 Node *Ty = getDerived().parseType();
5022 if (Ty == nullptr)
5023 return nullptr;
5024 return make<SpecialName>("typeinfo for ", Ty);
5025 }
5026 // TS <type> # typeinfo name (null-terminated byte string)
5027 case 'S': {
5028 First += 2;
5029 Node *Ty = getDerived().parseType();
5030 if (Ty == nullptr)
5031 return nullptr;
5032 return make<SpecialName>("typeinfo name for ", Ty);
5033 }
5034 // Tc <call-offset> <call-offset> <base encoding>
5035 case 'c': {
5036 First += 2;
5037 if (parseCallOffset() || parseCallOffset())
5038 return nullptr;
5039 Node *Encoding = getDerived().parseEncoding();
5040 if (Encoding == nullptr)
5041 return nullptr;
5042 return make<SpecialName>("covariant return thunk to ", Encoding);
5043 }
5044 // extension ::= TC <first type> <number> _ <second type>
5045 // # construction vtable for second-in-first
5046 case 'C': {
5047 First += 2;
5048 Node *FirstType = getDerived().parseType();
5049 if (FirstType == nullptr)
5050 return nullptr;
5051 if (parseNumber(true).empty() || !consumeIf('_'))
5052 return nullptr;
5053 Node *SecondType = getDerived().parseType();
5054 if (SecondType == nullptr)
5055 return nullptr;
5056 return make<CtorVtableSpecialName>(SecondType, FirstType);
5057 }
5058 // TW <object name> # Thread-local wrapper
5059 case 'W': {
5060 First += 2;
5061 Node *Name = getDerived().parseName();
5062 if (Name == nullptr)
5063 return nullptr;
5064 return make<SpecialName>("thread-local wrapper routine for ", Name);
5065 }
5066 // TH <object name> # Thread-local initialization
5067 case 'H': {
5068 First += 2;
5069 Node *Name = getDerived().parseName();
5070 if (Name == nullptr)
5071 return nullptr;
5072 return make<SpecialName>("thread-local initialization routine for ", Name);
5073 }
5074 // T <call-offset> <base encoding>
5075 default: {
5076 ++First;
5077 bool IsVirt = look() == 'v';
5078 if (parseCallOffset())
5079 return nullptr;
5080 Node *BaseEncoding = getDerived().parseEncoding();
5081 if (BaseEncoding == nullptr)
5082 return nullptr;
5083 if (IsVirt)
5084 return make<SpecialName>("virtual thunk to ", BaseEncoding);
5085 else
5086 return make<SpecialName>("non-virtual thunk to ", BaseEncoding);
5087 }
5088 }
5089 case 'G':
5090 switch (look(1)) {
5091 // GV <object name> # Guard variable for one-time initialization
5092 case 'V': {
5093 First += 2;
5094 Node *Name = getDerived().parseName();
5095 if (Name == nullptr)
5096 return nullptr;
5097 return make<SpecialName>("guard variable for ", Name);
5098 }
5099 // GR <object name> # reference temporary for object
5100 // GR <object name> _ # First temporary
5101 // GR <object name> <seq-id> _ # Subsequent temporaries
5102 case 'R': {
5103 First += 2;
5104 Node *Name = getDerived().parseName();
5105 if (Name == nullptr)
5106 return nullptr;
5107 size_t Count;
5108 bool ParsedSeqId = !parseSeqId(&Count);
5109 if (!consumeIf('_') && ParsedSeqId)
5110 return nullptr;
5111 return make<SpecialName>("reference temporary for ", Name);
5112 }
5113 }
5114 }
5115 return nullptr;
5116}
5117
5118// <encoding> ::= <function name> <bare-function-type>
5119// ::= <data name>
5120// ::= <special-name>
5121template <typename Derived, typename Alloc>
5122Node *AbstractManglingParser<Derived, Alloc>::parseEncoding() {
5123 if (look() == 'G' || look() == 'T')
5124 return getDerived().parseSpecialName();
5125
5126 auto IsEndOfEncoding = [&] {
5127 // The set of chars that can potentially follow an <encoding> (none of which
5128 // can start a <type>). Enumerating these allows us to avoid speculative
5129 // parsing.
5130 return numLeft() == 0 || look() == 'E' || look() == '.' || look() == '_';
5131 };
5132
5133 NameState NameInfo(this);
5134 Node *Name = getDerived().parseName(&NameInfo);
5135 if (Name == nullptr)
5136 return nullptr;
5137
5138 if (resolveForwardTemplateRefs(NameInfo))
5139 return nullptr;
5140
5141 if (IsEndOfEncoding())
5142 return Name;
5143
5144 Node *Attrs = nullptr;
5145 if (consumeIf("Ua9enable_ifI")) {
5146 size_t BeforeArgs = Names.size();
5147 while (!consumeIf('E')) {
5148 Node *Arg = getDerived().parseTemplateArg();
5149 if (Arg == nullptr)
5150 return nullptr;
5151 Names.push_back(Arg);
5152 }
5153 Attrs = make<EnableIfAttr>(popTrailingNodeArray(BeforeArgs));
5154 if (!Attrs)
5155 return nullptr;
5156 }
5157
5158 Node *ReturnType = nullptr;
5159 if (!NameInfo.CtorDtorConversion && NameInfo.EndsWithTemplateArgs) {
5160 ReturnType = getDerived().parseType();
5161 if (ReturnType == nullptr)
5162 return nullptr;
5163 }
5164
5165 if (consumeIf('v'))
5166 return make<FunctionEncoding>(ReturnType, Name, NodeArray(),
5167 Attrs, NameInfo.CVQualifiers,
5168 NameInfo.ReferenceQualifier);
5169
5170 size_t ParamsBegin = Names.size();
5171 do {
5172 Node *Ty = getDerived().parseType();
5173 if (Ty == nullptr)
5174 return nullptr;
5175 Names.push_back(Ty);
5176 } while (!IsEndOfEncoding());
5177
5178 return make<FunctionEncoding>(ReturnType, Name,
5179 popTrailingNodeArray(ParamsBegin),
5180 Attrs, NameInfo.CVQualifiers,
5181 NameInfo.ReferenceQualifier);
5182}
5183
5184template <class Float>
5185struct FloatData;
5186
5187template <>
5188struct FloatData<float>
5189{
5190 static const size_t mangled_size = 8;
5191 static const size_t max_demangled_size = 24;
5192 static constexpr const char* spec = "%af";
5193};
5194
5195template <>
5196struct FloatData<double>
5197{
5198 static const size_t mangled_size = 16;
5199 static const size_t max_demangled_size = 32;
5200 static constexpr const char* spec = "%a";
5201};
5202
5203template <>
5204struct FloatData<long double>
5205{
5206#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \
5207 defined(__wasm__)
5208 static const size_t mangled_size = 32;
5209#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__)
5210 static const size_t mangled_size = 16;
5211#else
5212 static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms
5213#endif
5214 static const size_t max_demangled_size = 40;
5215 static constexpr const char *spec = "%LaL";
5216};
5217
5218template <typename Alloc, typename Derived>
5219template <class Float>
5220Node *AbstractManglingParser<Alloc, Derived>::parseFloatingLiteral() {
5221 const size_t N = FloatData<Float>::mangled_size;
5222 if (numLeft() <= N)
5223 return nullptr;
5224 StringView Data(First, First + N);
5225 for (char C : Data)
5226 if (!std::isxdigit(C))
5227 return nullptr;
5228 First += N;
5229 if (!consumeIf('E'))
5230 return nullptr;
5231 return make<FloatLiteralImpl<Float>>(Data);
5232}
5233
5234// <seq-id> ::= <0-9A-Z>+
5235template <typename Alloc, typename Derived>
5236bool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) {
5237 if (!(look() >= '0' && look() <= '9') &&
5238 !(look() >= 'A' && look() <= 'Z'))
5239 return true;
5240
5241 size_t Id = 0;
5242 while (true) {
5243 if (look() >= '0' && look() <= '9') {
5244 Id *= 36;
5245 Id += static_cast<size_t>(look() - '0');
5246 } else if (look() >= 'A' && look() <= 'Z') {
5247 Id *= 36;
5248 Id += static_cast<size_t>(look() - 'A') + 10;
5249 } else {
5250 *Out = Id;
5251 return false;
5252 }
5253 ++First;
5254 }
5255}
5256
5257// <substitution> ::= S <seq-id> _
5258// ::= S_
5259// <substitution> ::= Sa # ::std::allocator
5260// <substitution> ::= Sb # ::std::basic_string
5261// <substitution> ::= Ss # ::std::basic_string < char,
5262// ::std::char_traits<char>,
5263// ::std::allocator<char> >
5264// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> >
5265// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> >
5266// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> >
5267template <typename Derived, typename Alloc>
5268Node *AbstractManglingParser<Derived, Alloc>::parseSubstitution() {
5269 if (!consumeIf('S'))
5270 return nullptr;
5271
5272 if (std::islower(look())) {
5273 Node *SpecialSub;
5274 switch (look()) {
5275 case 'a':
5276 ++First;
5277 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::allocator);
5278 break;
5279 case 'b':
5280 ++First;
5281 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::basic_string);
5282 break;
5283 case 's':
5284 ++First;
5285 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::string);
5286 break;
5287 case 'i':
5288 ++First;
5289 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::istream);
5290 break;
5291 case 'o':
5292 ++First;
5293 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::ostream);
5294 break;
5295 case 'd':
5296 ++First;
5297 SpecialSub = make<SpecialSubstitution>(SpecialSubKind::iostream);
5298 break;
5299 default:
5300 return nullptr;
5301 }
5302 if (!SpecialSub)
5303 return nullptr;
5304 // Itanium C++ ABI 5.1.2: If a name that would use a built-in <substitution>
5305 // has ABI tags, the tags are appended to the substitution; the result is a
5306 // substitutable component.
5307 Node *WithTags = getDerived().parseAbiTags(SpecialSub);
5308 if (WithTags != SpecialSub) {
5309 Subs.push_back(WithTags);
5310 SpecialSub = WithTags;
5311 }
5312 return SpecialSub;
5313 }
5314
5315 // ::= S_
5316 if (consumeIf('_')) {
5317 if (Subs.empty())
5318 return nullptr;
5319 return Subs[0];
5320 }
5321
5322 // ::= S <seq-id> _
5323 size_t Index = 0;
5324 if (parseSeqId(&Index))
5325 return nullptr;
5326 ++Index;
5327 if (!consumeIf('_') || Index >= Subs.size())
5328 return nullptr;
5329 return Subs[Index];
5330}
5331
5332// <template-param> ::= T_ # first template parameter
5333// ::= T <parameter-2 non-negative number> _
5334// ::= TL <level-1> __
5335// ::= TL <level-1> _ <parameter-2 non-negative number> _
5336template <typename Derived, typename Alloc>
5337Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParam() {
5338 if (!consumeIf('T'))
5339 return nullptr;
5340
5341 size_t Level = 0;
5342 if (consumeIf('L')) {
5343 if (parsePositiveInteger(&Level))
5344 return nullptr;
5345 ++Level;
5346 if (!consumeIf('_'))
5347 return nullptr;
5348 }
5349
5350 size_t Index = 0;
5351 if (!consumeIf('_')) {
5352 if (parsePositiveInteger(&Index))
5353 return nullptr;
5354 ++Index;
5355 if (!consumeIf('_'))
5356 return nullptr;
5357 }
5358
5359 // If we're in a context where this <template-param> refers to a
5360 // <template-arg> further ahead in the mangled name (currently just conversion
5361 // operator types), then we should only look it up in the right context.
5362 // This can only happen at the outermost level.
5363 if (PermitForwardTemplateReferences && Level == 0) {
5364 Node *ForwardRef = make<ForwardTemplateReference>(Index);
5365 if (!ForwardRef)
5366 return nullptr;
5367 assert(ForwardRef->getKind() == Node::KForwardTemplateReference);
5368 ForwardTemplateRefs.push_back(
5369 static_cast<ForwardTemplateReference *>(ForwardRef));
5370 return ForwardRef;
5371 }
5372
5373 if (Level >= TemplateParams.size() || !TemplateParams[Level] ||
5374 Index >= TemplateParams[Level]->size()) {
5375 // Itanium ABI 5.1.8: In a generic lambda, uses of auto in the parameter
5376 // list are mangled as the corresponding artificial template type parameter.
5377 if (ParsingLambdaParamsAtLevel == Level && Level <= TemplateParams.size()) {
5378 // This will be popped by the ScopedTemplateParamList in
5379 // parseUnnamedTypeName.
5380 if (Level == TemplateParams.size())
5381 TemplateParams.push_back(nullptr);
5382 return make<NameType>("auto");
5383 }
5384
5385 return nullptr;
5386 }
5387
5388 return (*TemplateParams[Level])[Index];
5389}
5390
5391// <template-param-decl> ::= Ty # type parameter
5392// ::= Tn <type> # non-type parameter
5393// ::= Tt <template-param-decl>* E # template parameter
5394// ::= Tp <template-param-decl> # parameter pack
5395template <typename Derived, typename Alloc>
5396Node *AbstractManglingParser<Derived, Alloc>::parseTemplateParamDecl() {
5397 auto InventTemplateParamName = [&](TemplateParamKind Kind) {
5398 unsigned Index = NumSyntheticTemplateParameters[(int)Kind]++;
5399 Node *N = make<SyntheticTemplateParamName>(Kind, Index);
5400 if (N) TemplateParams.back()->push_back(N);
5401 return N;
5402 };
5403
5404 if (consumeIf("Ty")) {
5405 Node *Name = InventTemplateParamName(TemplateParamKind::Type);
5406 if (!Name)
5407 return nullptr;
5408 return make<TypeTemplateParamDecl>(Name);
5409 }
5410
5411 if (consumeIf("Tn")) {
5412 Node *Name = InventTemplateParamName(TemplateParamKind::NonType);
5413 if (!Name)
5414 return nullptr;
5415 Node *Type = parseType();
5416 if (!Type)
5417 return nullptr;
5418 return make<NonTypeTemplateParamDecl>(Name, Type);
5419 }
5420
5421 if (consumeIf("Tt")) {
5422 Node *Name = InventTemplateParamName(TemplateParamKind::Template);
5423 if (!Name)
5424 return nullptr;
5425 size_t ParamsBegin = Names.size();
5426 ScopedTemplateParamList TemplateTemplateParamParams(this);
5427 while (!consumeIf("E")) {
5428 Node *P = parseTemplateParamDecl();
5429 if (!P)
5430 return nullptr;
5431 Names.push_back(P);
5432 }
5433 NodeArray Params = popTrailingNodeArray(ParamsBegin);
5434 return make<TemplateTemplateParamDecl>(Name, Params);
5435 }
5436
5437 if (consumeIf("Tp")) {
5438 Node *P = parseTemplateParamDecl();
5439 if (!P)
5440 return nullptr;
5441 return make<TemplateParamPackDecl>(P);
5442 }
5443
5444 return nullptr;
5445}
5446
5447// <template-arg> ::= <type> # type or template
5448// ::= X <expression> E # expression
5449// ::= <expr-primary> # simple expressions
5450// ::= J <template-arg>* E # argument pack
5451// ::= LZ <encoding> E # extension
5452template <typename Derived, typename Alloc>
5453Node *AbstractManglingParser<Derived, Alloc>::parseTemplateArg() {
5454 switch (look()) {
5455 case 'X': {
5456 ++First;
5457 Node *Arg = getDerived().parseExpr();
5458 if (Arg == nullptr || !consumeIf('E'))
5459 return nullptr;
5460 return Arg;
5461 }
5462 case 'J': {
5463 ++First;
5464 size_t ArgsBegin = Names.size();
5465 while (!consumeIf('E')) {
5466 Node *Arg = getDerived().parseTemplateArg();
5467 if (Arg == nullptr)
5468 return nullptr;
5469 Names.push_back(Arg);
5470 }
5471 NodeArray Args = popTrailingNodeArray(ArgsBegin);
5472 return make<TemplateArgumentPack>(Args);
5473 }
5474 case 'L': {
5475 // ::= LZ <encoding> E # extension
5476 if (look(1) == 'Z') {
5477 First += 2;
5478 Node *Arg = getDerived().parseEncoding();
5479 if (Arg == nullptr || !consumeIf('E'))
5480 return nullptr;
5481 return Arg;
5482 }
5483 // ::= <expr-primary> # simple expressions
5484 return getDerived().parseExprPrimary();
5485 }
5486 default:
5487 return getDerived().parseType();
5488 }
5489}
5490
5491// <template-args> ::= I <template-arg>* E
5492// extension, the abi says <template-arg>+
5493template <typename Derived, typename Alloc>
5494Node *
5495AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {
5496 if (!consumeIf('I'))
5497 return nullptr;
5498
5499 // <template-params> refer to the innermost <template-args>. Clear out any
5500 // outer args that we may have inserted into TemplateParams.
5501 if (TagTemplates) {
5502 TemplateParams.clear();
5503 TemplateParams.push_back(&OuterTemplateParams);
5504 OuterTemplateParams.clear();
5505 }
5506
5507 size_t ArgsBegin = Names.size();
5508 while (!consumeIf('E')) {
5509 if (TagTemplates) {
5510 auto OldParams = std::move(TemplateParams);
5511 Node *Arg = getDerived().parseTemplateArg();
5512 TemplateParams = std::move(OldParams);
5513 if (Arg == nullptr)
5514 return nullptr;
5515 Names.push_back(Arg);
5516 Node *TableEntry = Arg;
5517 if (Arg->getKind() == Node::KTemplateArgumentPack) {
5518 TableEntry = make<ParameterPack>(
5519 static_cast<TemplateArgumentPack*>(TableEntry)->getElements());
5520 if (!TableEntry)
5521 return nullptr;
5522 }
5523 TemplateParams.back()->push_back(TableEntry);
5524 } else {
5525 Node *Arg = getDerived().parseTemplateArg();
5526 if (Arg == nullptr)
5527 return nullptr;
5528 Names.push_back(Arg);
5529 }
5530 }
5531 return make<TemplateArgs>(popTrailingNodeArray(ArgsBegin));
5532}
5533
5534// <mangled-name> ::= _Z <encoding>
5535// ::= <type>
5536// extension ::= ___Z <encoding> _block_invoke
5537// extension ::= ___Z <encoding> _block_invoke<decimal-digit>+
5538// extension ::= ___Z <encoding> _block_invoke_<decimal-digit>+
5539template <typename Derived, typename Alloc>
5540Node *AbstractManglingParser<Derived, Alloc>::parse() {
5541 if (consumeIf("_Z") || consumeIf("__Z")) {
5542 Node *Encoding = getDerived().parseEncoding();
5543 if (Encoding == nullptr)
5544 return nullptr;
5545 if (look() == '.') {
5546 Encoding = make<DotSuffix>(Encoding, StringView(First, Last));
5547 First = Last;
5548 }
5549 if (numLeft() != 0)
5550 return nullptr;
5551 return Encoding;
5552 }
5553
5554 if (consumeIf("___Z") || consumeIf("____Z")) {
5555 Node *Encoding = getDerived().parseEncoding();
5556 if (Encoding == nullptr || !consumeIf("_block_invoke"))
5557 return nullptr;
5558 bool RequireNumber = consumeIf('_');
5559 if (parseNumber().empty() && RequireNumber)
5560 return nullptr;
5561 if (look() == '.')
5562 First = Last;
5563 if (numLeft() != 0)
5564 return nullptr;
5565 return make<SpecialName>("invocation function for block in ", Encoding);
5566 }
5567
5568 Node *Ty = getDerived().parseType();
5569 if (numLeft() != 0)
5570 return nullptr;
5571 return Ty;
5572}
5573
5574template <typename Alloc>
5575struct ManglingParser : AbstractManglingParser<ManglingParser<Alloc>, Alloc> {
5576 using AbstractManglingParser<ManglingParser<Alloc>,
5577 Alloc>::AbstractManglingParser;
5578};
5579
5580DEMANGLE_NAMESPACE_END
5581
5582#endif // DEMANGLE_ITANIUMDEMANGLE_H
diff --git a/externals/demangle/llvm/Demangle/StringView.h b/externals/demangle/llvm/Demangle/StringView.h
new file mode 100644
index 000000000..44d2b18a3
--- /dev/null
+++ b/externals/demangle/llvm/Demangle/StringView.h
@@ -0,0 +1,127 @@
1//===--- StringView.h -------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// FIXME: Use std::string_view instead when we support C++17.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef DEMANGLE_STRINGVIEW_H
15#define DEMANGLE_STRINGVIEW_H
16
17#include "DemangleConfig.h"
18#include <algorithm>
19#include <cassert>
20#include <cstring>
21
22DEMANGLE_NAMESPACE_BEGIN
23
24class StringView {
25 const char *First;
26 const char *Last;
27
28public:
29 static const size_t npos = ~size_t(0);
30
31 template <size_t N>
32 StringView(const char (&Str)[N]) : First(Str), Last(Str + N - 1) {}
33 StringView(const char *First_, const char *Last_)
34 : First(First_), Last(Last_) {}
35 StringView(const char *First_, size_t Len)
36 : First(First_), Last(First_ + Len) {}
37 StringView(const char *Str) : First(Str), Last(Str + std::strlen(Str)) {}
38 StringView() : First(nullptr), Last(nullptr) {}
39
40 StringView substr(size_t From) const {
41 return StringView(begin() + From, size() - From);
42 }
43
44 size_t find(char C, size_t From = 0) const {
45 size_t FindBegin = std::min(From, size());
46 // Avoid calling memchr with nullptr.
47 if (FindBegin < size()) {
48 // Just forward to memchr, which is faster than a hand-rolled loop.
49 if (const void *P = ::memchr(First + FindBegin, C, size() - FindBegin))
50 return size_t(static_cast<const char *>(P) - First);
51 }
52 return npos;
53 }
54
55 StringView substr(size_t From, size_t To) const {
56 if (To >= size())
57 To = size() - 1;
58 if (From >= size())
59 From = size() - 1;
60 return StringView(First + From, First + To);
61 }
62
63 StringView dropFront(size_t N = 1) const {
64 if (N >= size())
65 N = size();
66 return StringView(First + N, Last);
67 }
68
69 StringView dropBack(size_t N = 1) const {
70 if (N >= size())
71 N = size();
72 return StringView(First, Last - N);
73 }
74
75 char front() const {
76 assert(!empty());
77 return *begin();
78 }
79
80 char back() const {
81 assert(!empty());
82 return *(end() - 1);
83 }
84
85 char popFront() {
86 assert(!empty());
87 return *First++;
88 }
89
90 bool consumeFront(char C) {
91 if (!startsWith(C))
92 return false;
93 *this = dropFront(1);
94 return true;
95 }
96
97 bool consumeFront(StringView S) {
98 if (!startsWith(S))
99 return false;
100 *this = dropFront(S.size());
101 return true;
102 }
103
104 bool startsWith(char C) const { return !empty() && *begin() == C; }
105
106 bool startsWith(StringView Str) const {
107 if (Str.size() > size())
108 return false;
109 return std::equal(Str.begin(), Str.end(), begin());
110 }
111
112 const char &operator[](size_t Idx) const { return *(begin() + Idx); }
113
114 const char *begin() const { return First; }
115 const char *end() const { return Last; }
116 size_t size() const { return static_cast<size_t>(Last - First); }
117 bool empty() const { return First == Last; }
118};
119
120inline bool operator==(const StringView &LHS, const StringView &RHS) {
121 return LHS.size() == RHS.size() &&
122 std::equal(LHS.begin(), LHS.end(), RHS.begin());
123}
124
125DEMANGLE_NAMESPACE_END
126
127#endif
diff --git a/externals/demangle/llvm/Demangle/Utility.h b/externals/demangle/llvm/Demangle/Utility.h
new file mode 100644
index 000000000..50d05c6b1
--- /dev/null
+++ b/externals/demangle/llvm/Demangle/Utility.h
@@ -0,0 +1,192 @@
1//===--- Utility.h ----------------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-FileCopyrightText: Part of the LLVM Project
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// Provide some utility classes for use in the demangler(s).
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef DEMANGLE_UTILITY_H
15#define DEMANGLE_UTILITY_H
16
17#include "StringView.h"
18#include <cstdint>
19#include <cstdlib>
20#include <cstring>
21#include <iterator>
22#include <limits>
23
24DEMANGLE_NAMESPACE_BEGIN
25
26// Stream that AST nodes write their string representation into after the AST
27// has been parsed.
28class OutputStream {
29 char *Buffer;
30 size_t CurrentPosition;
31 size_t BufferCapacity;
32
33 // Ensure there is at least n more positions in buffer.
34 void grow(size_t N) {
35 if (N + CurrentPosition >= BufferCapacity) {
36 BufferCapacity *= 2;
37 if (BufferCapacity < N + CurrentPosition)
38 BufferCapacity = N + CurrentPosition;
39 Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
40 if (Buffer == nullptr)
41 std::terminate();
42 }
43 }
44
45 void writeUnsigned(uint64_t N, bool isNeg = false) {
46 // Handle special case...
47 if (N == 0) {
48 *this << '0';
49 return;
50 }
51
52 char Temp[21];
53 char *TempPtr = std::end(Temp);
54
55 while (N) {
56 *--TempPtr = '0' + char(N % 10);
57 N /= 10;
58 }
59
60 // Add negative sign...
61 if (isNeg)
62 *--TempPtr = '-';
63 this->operator<<(StringView(TempPtr, std::end(Temp)));
64 }
65
66public:
67 OutputStream(char *StartBuf, size_t Size)
68 : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
69 OutputStream() = default;
70 void reset(char *Buffer_, size_t BufferCapacity_) {
71 CurrentPosition = 0;
72 Buffer = Buffer_;
73 BufferCapacity = BufferCapacity_;
74 }
75
76 /// If a ParameterPackExpansion (or similar type) is encountered, the offset
77 /// into the pack that we're currently printing.
78 unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
79 unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
80
81 OutputStream &operator+=(StringView R) {
82 size_t Size = R.size();
83 if (Size == 0)
84 return *this;
85 grow(Size);
86 std::memmove(Buffer + CurrentPosition, R.begin(), Size);
87 CurrentPosition += Size;
88 return *this;
89 }
90
91 OutputStream &operator+=(char C) {
92 grow(1);
93 Buffer[CurrentPosition++] = C;
94 return *this;
95 }
96
97 OutputStream &operator<<(StringView R) { return (*this += R); }
98
99 OutputStream &operator<<(char C) { return (*this += C); }
100
101 OutputStream &operator<<(long long N) {
102 if (N < 0)
103 writeUnsigned(static_cast<unsigned long long>(-N), true);
104 else
105 writeUnsigned(static_cast<unsigned long long>(N));
106 return *this;
107 }
108
109 OutputStream &operator<<(unsigned long long N) {
110 writeUnsigned(N, false);
111 return *this;
112 }
113
114 OutputStream &operator<<(long N) {
115 return this->operator<<(static_cast<long long>(N));
116 }
117
118 OutputStream &operator<<(unsigned long N) {
119 return this->operator<<(static_cast<unsigned long long>(N));
120 }
121
122 OutputStream &operator<<(int N) {
123 return this->operator<<(static_cast<long long>(N));
124 }
125
126 OutputStream &operator<<(unsigned int N) {
127 return this->operator<<(static_cast<unsigned long long>(N));
128 }
129
130 size_t getCurrentPosition() const { return CurrentPosition; }
131 void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
132
133 char back() const {
134 return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
135 }
136
137 bool empty() const { return CurrentPosition == 0; }
138
139 char *getBuffer() { return Buffer; }
140 char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
141 size_t getBufferCapacity() { return BufferCapacity; }
142};
143
144template <class T> class SwapAndRestore {
145 T &Restore;
146 T OriginalValue;
147 bool ShouldRestore = true;
148
149public:
150 SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {}
151
152 SwapAndRestore(T &Restore_, T NewVal)
153 : Restore(Restore_), OriginalValue(Restore) {
154 Restore = std::move(NewVal);
155 }
156 ~SwapAndRestore() {
157 if (ShouldRestore)
158 Restore = std::move(OriginalValue);
159 }
160
161 void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; }
162
163 void restoreNow(bool Force) {
164 if (!Force && !ShouldRestore)
165 return;
166
167 Restore = std::move(OriginalValue);
168 ShouldRestore = false;
169 }
170
171 SwapAndRestore(const SwapAndRestore &) = delete;
172 SwapAndRestore &operator=(const SwapAndRestore &) = delete;
173};
174
175inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S,
176 size_t InitSize) {
177 size_t BufferSize;
178 if (Buf == nullptr) {
179 Buf = static_cast<char *>(std::malloc(InitSize));
180 if (Buf == nullptr)
181 return false;
182 BufferSize = InitSize;
183 } else
184 BufferSize = *N;
185
186 S.reset(Buf, BufferSize);
187 return true;
188}
189
190DEMANGLE_NAMESPACE_END
191
192#endif
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 45332cf95..9884a4a0b 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -38,6 +38,8 @@ add_library(common STATIC
38 common_precompiled_headers.h 38 common_precompiled_headers.h
39 common_types.h 39 common_types.h
40 concepts.h 40 concepts.h
41 demangle.cpp
42 demangle.h
41 div_ceil.h 43 div_ceil.h
42 dynamic_library.cpp 44 dynamic_library.cpp
43 dynamic_library.h 45 dynamic_library.h
@@ -175,7 +177,7 @@ endif()
175create_target_directory_groups(common) 177create_target_directory_groups(common)
176 178
177target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads) 179target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile Threads::Threads)
178target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd) 180target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
179 181
180if (YUZU_USE_PRECOMPILED_HEADERS) 182if (YUZU_USE_PRECOMPILED_HEADERS)
181 target_precompile_headers(common PRIVATE precompiled_headers.h) 183 target_precompile_headers(common PRIVATE precompiled_headers.h)
diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp
new file mode 100644
index 000000000..3310faf86
--- /dev/null
+++ b/src/common/demangle.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <llvm/Demangle/Demangle.h>
5
6#include "common/demangle.h"
7#include "common/scope_exit.h"
8
9namespace Common {
10
11std::string DemangleSymbol(const std::string& mangled) {
12 auto is_itanium = [](const std::string& name) -> bool {
13 // A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
14 auto pos = name.find_first_not_of('_');
15 return pos > 0 && pos <= 4 && pos < name.size() && name[pos] == 'Z';
16 };
17
18 if (mangled.empty()) {
19 return mangled;
20 }
21
22 char* demangled = nullptr;
23 SCOPE_EXIT({ std::free(demangled); });
24
25 if (is_itanium(mangled)) {
26 demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr);
27 }
28
29 if (!demangled) {
30 return mangled;
31 }
32 return demangled;
33}
34
35} // namespace Common
diff --git a/src/common/demangle.h b/src/common/demangle.h
new file mode 100644
index 000000000..f072d22f3
--- /dev/null
+++ b/src/common/demangle.h
@@ -0,0 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8namespace Common {
9
10std::string DemangleSymbol(const std::string& mangled);
11
12} // namespace Common
diff --git a/src/common/input.h b/src/common/input.h
index d27b1d772..d61cd7ca8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -51,6 +51,8 @@ enum class PollingMode {
51 NFC, 51 NFC,
52 // Enable infrared camera polling 52 // Enable infrared camera polling
53 IR, 53 IR,
54 // Enable ring controller polling
55 Ring,
54}; 56};
55 57
56enum class CameraFormat { 58enum class CameraFormat {
@@ -62,21 +64,22 @@ enum class CameraFormat {
62 None, 64 None,
63}; 65};
64 66
65// Vibration reply from the controller 67// Different results that can happen from a device request
66enum class VibrationError { 68enum class DriverResult {
67 None, 69 Success,
70 WrongReply,
71 Timeout,
72 UnsupportedControllerType,
73 HandleInUse,
74 ErrorReadingData,
75 ErrorWritingData,
76 NoDeviceDetected,
77 InvalidHandle,
68 NotSupported, 78 NotSupported,
69 Disabled, 79 Disabled,
70 Unknown, 80 Unknown,
71}; 81};
72 82
73// Polling mode reply from the controller
74enum class PollingError {
75 None,
76 NotSupported,
77 Unknown,
78};
79
80// Nfc reply from the controller 83// Nfc reply from the controller
81enum class NfcState { 84enum class NfcState {
82 Success, 85 Success,
@@ -90,13 +93,6 @@ enum class NfcState {
90 Unknown, 93 Unknown,
91}; 94};
92 95
93// Ir camera reply from the controller
94enum class CameraError {
95 None,
96 NotSupported,
97 Unknown,
98};
99
100// Hint for amplification curve to be used 96// Hint for amplification curve to be used
101enum class VibrationAmplificationType { 97enum class VibrationAmplificationType {
102 Linear, 98 Linear,
@@ -190,6 +186,8 @@ struct TouchStatus {
190struct BodyColorStatus { 186struct BodyColorStatus {
191 u32 body{}; 187 u32 body{};
192 u32 buttons{}; 188 u32 buttons{};
189 u32 left_grip{};
190 u32 right_grip{};
193}; 191};
194 192
195// HD rumble data 193// HD rumble data
@@ -228,17 +226,31 @@ enum class ButtonNames {
228 Engine, 226 Engine,
229 // This will display the button by value instead of the button name 227 // This will display the button by value instead of the button name
230 Value, 228 Value,
229
230 // Joycon button names
231 ButtonLeft, 231 ButtonLeft,
232 ButtonRight, 232 ButtonRight,
233 ButtonDown, 233 ButtonDown,
234 ButtonUp, 234 ButtonUp,
235 TriggerZ,
236 TriggerR,
237 TriggerL,
238 ButtonA, 235 ButtonA,
239 ButtonB, 236 ButtonB,
240 ButtonX, 237 ButtonX,
241 ButtonY, 238 ButtonY,
239 ButtonPlus,
240 ButtonMinus,
241 ButtonHome,
242 ButtonCapture,
243 ButtonStickL,
244 ButtonStickR,
245 TriggerL,
246 TriggerZL,
247 TriggerSL,
248 TriggerR,
249 TriggerZR,
250 TriggerSR,
251
252 // GC button names
253 TriggerZ,
242 ButtonStart, 254 ButtonStart,
243 255
244 // DS4 button names 256 // DS4 button names
@@ -316,22 +328,24 @@ class OutputDevice {
316public: 328public:
317 virtual ~OutputDevice() = default; 329 virtual ~OutputDevice() = default;
318 330
319 virtual void SetLED([[maybe_unused]] const LedStatus& led_status) {} 331 virtual DriverResult SetLED([[maybe_unused]] const LedStatus& led_status) {
332 return DriverResult::NotSupported;
333 }
320 334
321 virtual VibrationError SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) { 335 virtual DriverResult SetVibration([[maybe_unused]] const VibrationStatus& vibration_status) {
322 return VibrationError::NotSupported; 336 return DriverResult::NotSupported;
323 } 337 }
324 338
325 virtual bool IsVibrationEnabled() { 339 virtual bool IsVibrationEnabled() {
326 return false; 340 return false;
327 } 341 }
328 342
329 virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) { 343 virtual DriverResult SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
330 return PollingError::NotSupported; 344 return DriverResult::NotSupported;
331 } 345 }
332 346
333 virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) { 347 virtual DriverResult SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
334 return CameraError::NotSupported; 348 return DriverResult::NotSupported;
335 } 349 }
336 350
337 virtual NfcState SupportsNfc() const { 351 virtual NfcState SupportsNfc() const {
diff --git a/src/common/settings.h b/src/common/settings.h
index 80b2eeabc..4b4da4da2 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -483,6 +483,7 @@ struct Values {
483 483
484 Setting<bool> enable_raw_input{false, "enable_raw_input"}; 484 Setting<bool> enable_raw_input{false, "enable_raw_input"};
485 Setting<bool> controller_navigation{true, "controller_navigation"}; 485 Setting<bool> controller_navigation{true, "controller_navigation"};
486 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
486 487
487 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; 488 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
488 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 489 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2df7b0ee8..8aa7b9641 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,14 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#ifndef _MSC_VER
5#include <cxxabi.h>
6#endif
7
8#include <map> 4#include <map>
9#include <optional> 5#include <optional>
6
10#include "common/bit_field.h" 7#include "common/bit_field.h"
11#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/demangle.h"
12#include "common/logging/log.h" 10#include "common/logging/log.h"
13#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
14#include "core/arm/symbols.h" 12#include "core/arm/symbols.h"
@@ -71,20 +69,8 @@ void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<Backt
71 const auto symbol_set = symbols.find(entry.module); 69 const auto symbol_set = symbols.find(entry.module);
72 if (symbol_set != symbols.end()) { 70 if (symbol_set != symbols.end()) {
73 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset); 71 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
74 if (symbol.has_value()) { 72 if (symbol) {
75#ifdef _MSC_VER 73 entry.name = Common::DemangleSymbol(*symbol);
76 // TODO(DarkLordZach): Add demangling of symbol names.
77 entry.name = *symbol;
78#else
79 int status{-1};
80 char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
81 if (status == 0 && demangled != nullptr) {
82 entry.name = demangled;
83 std::free(demangled);
84 } else {
85 entry.name = *symbol;
86 }
87#endif
88 } 74 }
89 } 75 }
90 } 76 }
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7a01f3f4c..0e06468da 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <common/scope_exit.h>
5 6
6#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
7#include "common/thread.h" 8#include "common/thread.h"
@@ -93,6 +94,7 @@ void EmulatedController::ReloadFromSettings() {
93 motion_params[index] = Common::ParamPackage(player.motions[index]); 94 motion_params[index] = Common::ParamPackage(player.motions[index]);
94 } 95 }
95 96
97 controller.color_values = {};
96 controller.colors_state.fullkey = { 98 controller.colors_state.fullkey = {
97 .body = GetNpadColor(player.body_color_left), 99 .body = GetNpadColor(player.body_color_left),
98 .button = GetNpadColor(player.button_color_left), 100 .button = GetNpadColor(player.button_color_left),
@@ -106,6 +108,8 @@ void EmulatedController::ReloadFromSettings() {
106 .button = GetNpadColor(player.button_color_right), 108 .button = GetNpadColor(player.button_color_right),
107 }; 109 };
108 110
111 ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs);
112
109 // Other or debug controller should always be a pro controller 113 // Other or debug controller should always be a pro controller
110 if (npad_id_type != NpadIdType::Other) { 114 if (npad_id_type != NpadIdType::Other) {
111 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type)); 115 SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
@@ -132,18 +136,28 @@ void EmulatedController::LoadDevices() {
132 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL]; 136 trigger_params[LeftIndex] = button_params[Settings::NativeButton::ZL];
133 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR]; 137 trigger_params[RightIndex] = button_params[Settings::NativeButton::ZR];
134 138
139 color_params[LeftIndex] = left_joycon;
140 color_params[RightIndex] = right_joycon;
141 color_params[LeftIndex].Set("color", true);
142 color_params[RightIndex].Set("color", true);
143
135 battery_params[LeftIndex] = left_joycon; 144 battery_params[LeftIndex] = left_joycon;
136 battery_params[RightIndex] = right_joycon; 145 battery_params[RightIndex] = right_joycon;
137 battery_params[LeftIndex].Set("battery", true); 146 battery_params[LeftIndex].Set("battery", true);
138 battery_params[RightIndex].Set("battery", true); 147 battery_params[RightIndex].Set("battery", true);
139 148
140 camera_params = Common::ParamPackage{"engine:camera,camera:1"}; 149 camera_params[0] = right_joycon;
141 nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"}; 150 camera_params[0].Set("camera", true);
151 camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};
152 ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};
153 nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
154 nfc_params[1] = right_joycon;
155 nfc_params[1].Set("nfc", true);
142 156
143 output_params[LeftIndex] = left_joycon; 157 output_params[LeftIndex] = left_joycon;
144 output_params[RightIndex] = right_joycon; 158 output_params[RightIndex] = right_joycon;
145 output_params[2] = camera_params; 159 output_params[2] = camera_params[1];
146 output_params[3] = nfc_params; 160 output_params[3] = nfc_params[0];
147 output_params[LeftIndex].Set("output", true); 161 output_params[LeftIndex].Set("output", true);
148 output_params[RightIndex].Set("output", true); 162 output_params[RightIndex].Set("output", true);
149 output_params[2].Set("output", true); 163 output_params[2].Set("output", true);
@@ -159,8 +173,11 @@ void EmulatedController::LoadDevices() {
159 Common::Input::CreateInputDevice); 173 Common::Input::CreateInputDevice);
160 std::ranges::transform(battery_params, battery_devices.begin(), 174 std::ranges::transform(battery_params, battery_devices.begin(),
161 Common::Input::CreateInputDevice); 175 Common::Input::CreateInputDevice);
162 camera_devices = Common::Input::CreateInputDevice(camera_params); 176 std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice);
163 nfc_devices = Common::Input::CreateInputDevice(nfc_params); 177 std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);
178 std::ranges::transform(ring_params, ring_analog_devices.begin(),
179 Common::Input::CreateInputDevice);
180 std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice);
164 std::ranges::transform(output_params, output_devices.begin(), 181 std::ranges::transform(output_params, output_devices.begin(),
165 Common::Input::CreateOutputDevice); 182 Common::Input::CreateOutputDevice);
166 183
@@ -322,6 +339,19 @@ void EmulatedController::ReloadInput() {
322 battery_devices[index]->ForceUpdate(); 339 battery_devices[index]->ForceUpdate();
323 } 340 }
324 341
342 for (std::size_t index = 0; index < color_devices.size(); ++index) {
343 if (!color_devices[index]) {
344 continue;
345 }
346 color_devices[index]->SetCallback({
347 .on_change =
348 [this, index](const Common::Input::CallbackStatus& callback) {
349 SetColors(callback, index);
350 },
351 });
352 color_devices[index]->ForceUpdate();
353 }
354
325 for (std::size_t index = 0; index < motion_devices.size(); ++index) { 355 for (std::size_t index = 0; index < motion_devices.size(); ++index) {
326 if (!motion_devices[index]) { 356 if (!motion_devices[index]) {
327 continue; 357 continue;
@@ -335,22 +365,37 @@ void EmulatedController::ReloadInput() {
335 motion_devices[index]->ForceUpdate(); 365 motion_devices[index]->ForceUpdate();
336 } 366 }
337 367
338 if (camera_devices) { 368 for (std::size_t index = 0; index < camera_devices.size(); ++index) {
339 camera_devices->SetCallback({ 369 if (!camera_devices[index]) {
370 continue;
371 }
372 camera_devices[index]->SetCallback({
340 .on_change = 373 .on_change =
341 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); }, 374 [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
342 }); 375 });
343 camera_devices->ForceUpdate(); 376 camera_devices[index]->ForceUpdate();
344 } 377 }
345 378
346 if (nfc_devices) { 379 for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) {
347 if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) { 380 if (!ring_analog_devices[index]) {
348 nfc_devices->SetCallback({ 381 continue;
349 .on_change =
350 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
351 });
352 nfc_devices->ForceUpdate();
353 } 382 }
383 ring_analog_devices[index]->SetCallback({
384 .on_change =
385 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
386 });
387 ring_analog_devices[index]->ForceUpdate();
388 }
389
390 for (std::size_t index = 0; index < nfc_devices.size(); ++index) {
391 if (!nfc_devices[index]) {
392 continue;
393 }
394 nfc_devices[index]->SetCallback({
395 .on_change =
396 [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
397 });
398 nfc_devices[index]->ForceUpdate();
354 } 399 }
355 400
356 // Register TAS devices. No need to force update 401 // Register TAS devices. No need to force update
@@ -420,6 +465,9 @@ void EmulatedController::UnloadInput() {
420 for (auto& battery : battery_devices) { 465 for (auto& battery : battery_devices) {
421 battery.reset(); 466 battery.reset();
422 } 467 }
468 for (auto& color : color_devices) {
469 color.reset();
470 }
423 for (auto& output : output_devices) { 471 for (auto& output : output_devices) {
424 output.reset(); 472 output.reset();
425 } 473 }
@@ -435,8 +483,15 @@ void EmulatedController::UnloadInput() {
435 for (auto& stick : virtual_stick_devices) { 483 for (auto& stick : virtual_stick_devices) {
436 stick.reset(); 484 stick.reset();
437 } 485 }
438 camera_devices.reset(); 486 for (auto& camera : camera_devices) {
439 nfc_devices.reset(); 487 camera.reset();
488 }
489 for (auto& ring : ring_analog_devices) {
490 ring.reset();
491 }
492 for (auto& nfc : nfc_devices) {
493 nfc.reset();
494 }
440} 495}
441 496
442void EmulatedController::EnableConfiguration() { 497void EmulatedController::EnableConfiguration() {
@@ -448,6 +503,11 @@ void EmulatedController::EnableConfiguration() {
448void EmulatedController::DisableConfiguration() { 503void EmulatedController::DisableConfiguration() {
449 is_configuring = false; 504 is_configuring = false;
450 505
506 // Get Joycon colors before turning on the controller
507 for (const auto& color_device : color_devices) {
508 color_device->ForceUpdate();
509 }
510
451 // Apply temporary npad type to the real controller 511 // Apply temporary npad type to the real controller
452 if (tmp_npad_type != npad_type) { 512 if (tmp_npad_type != npad_type) {
453 if (is_connected) { 513 if (is_connected) {
@@ -501,6 +561,9 @@ void EmulatedController::SaveCurrentConfig() {
501 for (std::size_t index = 0; index < player.motions.size(); ++index) { 561 for (std::size_t index = 0; index < player.motions.size(); ++index) {
502 player.motions[index] = motion_params[index].Serialize(); 562 player.motions[index] = motion_params[index].Serialize();
503 } 563 }
564 if (npad_id_type == NpadIdType::Player1) {
565 Settings::values.ringcon_analogs = ring_params[0].Serialize();
566 }
504} 567}
505 568
506void EmulatedController::RestoreConfig() { 569void EmulatedController::RestoreConfig() {
@@ -772,17 +835,21 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
772 if (index >= controller.stick_values.size()) { 835 if (index >= controller.stick_values.size()) {
773 return; 836 return;
774 } 837 }
775 std::unique_lock lock{mutex}; 838 auto trigger_guard =
839 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Stick, !is_configuring); });
840 std::scoped_lock lock{mutex};
776 const auto stick_value = TransformToStick(callback); 841 const auto stick_value = TransformToStick(callback);
777 842
778 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 843 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
779 if (controller.stick_values[index].uuid != uuid) { 844 if (controller.stick_values[index].uuid != uuid) {
780 const bool is_tas = uuid == TAS_UUID; 845 const bool is_tas = uuid == TAS_UUID;
781 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) { 846 if (is_tas && stick_value.x.value == 0 && stick_value.y.value == 0) {
847 trigger_guard.Cancel();
782 return; 848 return;
783 } 849 }
784 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left && 850 if (!is_tas && !stick_value.down && !stick_value.up && !stick_value.left &&
785 !stick_value.right) { 851 !stick_value.right) {
852 trigger_guard.Cancel();
786 return; 853 return;
787 } 854 }
788 } 855 }
@@ -793,8 +860,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
793 if (is_configuring) { 860 if (is_configuring) {
794 controller.analog_stick_state.left = {}; 861 controller.analog_stick_state.left = {};
795 controller.analog_stick_state.right = {}; 862 controller.analog_stick_state.right = {};
796 lock.unlock();
797 TriggerOnChange(ControllerTriggerType::Stick, false);
798 return; 863 return;
799 } 864 }
800 865
@@ -819,9 +884,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
819 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down); 884 controller.npad_button_state.stick_r_down.Assign(controller.stick_values[index].down);
820 break; 885 break;
821 } 886 }
822
823 lock.unlock();
824 TriggerOnChange(ControllerTriggerType::Stick, true);
825} 887}
826 888
827void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback, 889void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callback,
@@ -829,7 +891,9 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
829 if (index >= controller.trigger_values.size()) { 891 if (index >= controller.trigger_values.size()) {
830 return; 892 return;
831 } 893 }
832 std::unique_lock lock{mutex}; 894 auto trigger_guard =
895 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring); });
896 std::scoped_lock lock{mutex};
833 const auto trigger_value = TransformToTrigger(callback); 897 const auto trigger_value = TransformToTrigger(callback);
834 898
835 // Only read trigger values that have the same uuid or are pressed once 899 // Only read trigger values that have the same uuid or are pressed once
@@ -845,13 +909,12 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
845 if (is_configuring) { 909 if (is_configuring) {
846 controller.gc_trigger_state.left = 0; 910 controller.gc_trigger_state.left = 0;
847 controller.gc_trigger_state.right = 0; 911 controller.gc_trigger_state.right = 0;
848 lock.unlock();
849 TriggerOnChange(ControllerTriggerType::Trigger, false);
850 return; 912 return;
851 } 913 }
852 914
853 // Only GC controllers have analog triggers 915 // Only GC controllers have analog triggers
854 if (npad_type != NpadStyleIndex::GameCube) { 916 if (npad_type != NpadStyleIndex::GameCube) {
917 trigger_guard.Cancel();
855 return; 918 return;
856 } 919 }
857 920
@@ -868,9 +931,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
868 controller.npad_button_state.zr.Assign(trigger.pressed.value); 931 controller.npad_button_state.zr.Assign(trigger.pressed.value);
869 break; 932 break;
870 } 933 }
871
872 lock.unlock();
873 TriggerOnChange(ControllerTriggerType::Trigger, true);
874} 934}
875 935
876void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback, 936void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback,
@@ -878,7 +938,8 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
878 if (index >= controller.motion_values.size()) { 938 if (index >= controller.motion_values.size()) {
879 return; 939 return;
880 } 940 }
881 std::unique_lock lock{mutex}; 941 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Motion, !is_configuring); });
942 std::scoped_lock lock{mutex};
882 auto& raw_status = controller.motion_values[index].raw_status; 943 auto& raw_status = controller.motion_values[index].raw_status;
883 auto& emulated = controller.motion_values[index].emulated; 944 auto& emulated = controller.motion_values[index].emulated;
884 945
@@ -899,8 +960,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
899 force_update_motion = raw_status.force_update; 960 force_update_motion = raw_status.force_update;
900 961
901 if (is_configuring) { 962 if (is_configuring) {
902 lock.unlock();
903 TriggerOnChange(ControllerTriggerType::Motion, false);
904 return; 963 return;
905 } 964 }
906 965
@@ -910,9 +969,56 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
910 motion.rotation = emulated.GetRotations(); 969 motion.rotation = emulated.GetRotations();
911 motion.orientation = emulated.GetOrientation(); 970 motion.orientation = emulated.GetOrientation();
912 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 971 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
972}
913 973
914 lock.unlock(); 974void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback,
915 TriggerOnChange(ControllerTriggerType::Motion, true); 975 std::size_t index) {
976 if (index >= controller.color_values.size()) {
977 return;
978 }
979 auto trigger_guard =
980 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Color, !is_configuring); });
981 std::scoped_lock lock{mutex};
982 controller.color_values[index] = TransformToColor(callback);
983
984 if (is_configuring) {
985 return;
986 }
987
988 if (controller.color_values[index].body == 0) {
989 trigger_guard.Cancel();
990 return;
991 }
992
993 controller.colors_state.fullkey = {
994 .body = GetNpadColor(controller.color_values[index].body),
995 .button = GetNpadColor(controller.color_values[index].buttons),
996 };
997 if (npad_type == NpadStyleIndex::ProController) {
998 controller.colors_state.left = {
999 .body = GetNpadColor(controller.color_values[index].left_grip),
1000 .button = GetNpadColor(controller.color_values[index].buttons),
1001 };
1002 controller.colors_state.right = {
1003 .body = GetNpadColor(controller.color_values[index].right_grip),
1004 .button = GetNpadColor(controller.color_values[index].buttons),
1005 };
1006 } else {
1007 switch (index) {
1008 case LeftIndex:
1009 controller.colors_state.left = {
1010 .body = GetNpadColor(controller.color_values[index].body),
1011 .button = GetNpadColor(controller.color_values[index].buttons),
1012 };
1013 break;
1014 case RightIndex:
1015 controller.colors_state.right = {
1016 .body = GetNpadColor(controller.color_values[index].body),
1017 .button = GetNpadColor(controller.color_values[index].buttons),
1018 };
1019 break;
1020 }
1021 }
916} 1022}
917 1023
918void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, 1024void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
@@ -920,12 +1026,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
920 if (index >= controller.battery_values.size()) { 1026 if (index >= controller.battery_values.size()) {
921 return; 1027 return;
922 } 1028 }
923 std::unique_lock lock{mutex}; 1029 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Battery, !is_configuring); });
1030 std::scoped_lock lock{mutex};
924 controller.battery_values[index] = TransformToBattery(callback); 1031 controller.battery_values[index] = TransformToBattery(callback);
925 1032
926 if (is_configuring) { 1033 if (is_configuring) {
927 lock.unlock();
928 TriggerOnChange(ControllerTriggerType::Battery, false);
929 return; 1034 return;
930 } 1035 }
931 1036
@@ -981,18 +1086,14 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
981 }; 1086 };
982 break; 1087 break;
983 } 1088 }
984
985 lock.unlock();
986 TriggerOnChange(ControllerTriggerType::Battery, true);
987} 1089}
988 1090
989void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) { 1091void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
990 std::unique_lock lock{mutex}; 1092 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring); });
1093 std::scoped_lock lock{mutex};
991 controller.camera_values = TransformToCamera(callback); 1094 controller.camera_values = TransformToCamera(callback);
992 1095
993 if (is_configuring) { 1096 if (is_configuring) {
994 lock.unlock();
995 TriggerOnChange(ControllerTriggerType::IrSensor, false);
996 return; 1097 return;
997 } 1098 }
998 1099
@@ -1000,18 +1101,28 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
1000 controller.camera_state.format = 1101 controller.camera_state.format =
1001 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format); 1102 static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
1002 controller.camera_state.data = controller.camera_values.data; 1103 controller.camera_state.data = controller.camera_values.data;
1104}
1003 1105
1004 lock.unlock(); 1106void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
1005 TriggerOnChange(ControllerTriggerType::IrSensor, true); 1107 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::RingController, !is_configuring); });
1108 std::scoped_lock lock{mutex};
1109 const auto force_value = TransformToStick(callback);
1110
1111 controller.ring_analog_value = force_value.x;
1112
1113 if (is_configuring) {
1114 return;
1115 }
1116
1117 controller.ring_analog_state.force = force_value.x.value;
1006} 1118}
1007 1119
1008void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) { 1120void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1009 std::unique_lock lock{mutex}; 1121 SCOPE_EXIT({ TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring); });
1122 std::scoped_lock lock{mutex};
1010 controller.nfc_values = TransformToNfc(callback); 1123 controller.nfc_values = TransformToNfc(callback);
1011 1124
1012 if (is_configuring) { 1125 if (is_configuring) {
1013 lock.unlock();
1014 TriggerOnChange(ControllerTriggerType::Nfc, false);
1015 return; 1126 return;
1016 } 1127 }
1017 1128
@@ -1019,9 +1130,6 @@ void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
1019 controller.nfc_values.state, 1130 controller.nfc_values.state,
1020 controller.nfc_values.data, 1131 controller.nfc_values.data,
1021 }; 1132 };
1022
1023 lock.unlock();
1024 TriggerOnChange(ControllerTriggerType::Nfc, true);
1025} 1133}
1026 1134
1027bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) { 1135bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
@@ -1053,7 +1161,7 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
1053 .type = type, 1161 .type = type,
1054 }; 1162 };
1055 return output_devices[device_index]->SetVibration(status) == 1163 return output_devices[device_index]->SetVibration(status) ==
1056 Common::Input::VibrationError::None; 1164 Common::Input::DriverResult::Success;
1057} 1165}
1058 1166
1059bool EmulatedController::IsVibrationEnabled(std::size_t device_index) { 1167bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
@@ -1075,16 +1183,32 @@ bool EmulatedController::IsVibrationEnabled(std::size_t device_index) {
1075 return output_devices[device_index]->IsVibrationEnabled(); 1183 return output_devices[device_index]->IsVibrationEnabled();
1076} 1184}
1077 1185
1078bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) { 1186Common::Input::DriverResult EmulatedController::SetPollingMode(
1079 LOG_INFO(Service_HID, "Set polling mode {}", polling_mode); 1187 EmulatedDeviceIndex device_index, Common::Input::PollingMode polling_mode) {
1080 auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)]; 1188 LOG_INFO(Service_HID, "Set polling mode {}, device_index={}", polling_mode, device_index);
1189
1190 auto& left_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Left)];
1191 auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
1081 auto& nfc_output_device = output_devices[3]; 1192 auto& nfc_output_device = output_devices[3];
1082 1193
1083 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode); 1194 if (device_index == EmulatedDeviceIndex::LeftIndex) {
1084 const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode); 1195 return left_output_device->SetPollingMode(polling_mode);
1196 }
1197
1198 if (device_index == EmulatedDeviceIndex::RightIndex) {
1199 const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
1200 const auto mapped_nfc_result = right_output_device->SetPollingMode(polling_mode);
1201
1202 if (virtual_nfc_result == Common::Input::DriverResult::Success) {
1203 return virtual_nfc_result;
1204 }
1205 return mapped_nfc_result;
1206 }
1085 1207
1086 return virtual_nfc_result == Common::Input::PollingError::None || 1208 left_output_device->SetPollingMode(polling_mode);
1087 mapped_nfc_result == Common::Input::PollingError::None; 1209 right_output_device->SetPollingMode(polling_mode);
1210 nfc_output_device->SetPollingMode(polling_mode);
1211 return Common::Input::DriverResult::Success;
1088} 1212}
1089 1213
1090bool EmulatedController::SetCameraFormat( 1214bool EmulatedController::SetCameraFormat(
@@ -1095,13 +1219,22 @@ bool EmulatedController::SetCameraFormat(
1095 auto& camera_output_device = output_devices[2]; 1219 auto& camera_output_device = output_devices[2];
1096 1220
1097 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1221 if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1098 camera_format)) == Common::Input::CameraError::None) { 1222 camera_format)) == Common::Input::DriverResult::Success) {
1099 return true; 1223 return true;
1100 } 1224 }
1101 1225
1102 // Fallback to Qt camera if native device doesn't have support 1226 // Fallback to Qt camera if native device doesn't have support
1103 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>( 1227 return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
1104 camera_format)) == Common::Input::CameraError::None; 1228 camera_format)) == Common::Input::DriverResult::Success;
1229}
1230
1231Common::ParamPackage EmulatedController::GetRingParam() const {
1232 return ring_params[0];
1233}
1234
1235void EmulatedController::SetRingParam(Common::ParamPackage param) {
1236 ring_params[0] = std::move(param);
1237 ReloadInput();
1105} 1238}
1106 1239
1107bool EmulatedController::HasNfc() const { 1240bool EmulatedController::HasNfc() const {
@@ -1255,39 +1388,35 @@ void EmulatedController::Connect(bool use_temporary_value) {
1255 return; 1388 return;
1256 } 1389 }
1257 1390
1258 std::unique_lock lock{mutex}; 1391 auto trigger_guard =
1392 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
1393 std::scoped_lock lock{mutex};
1259 if (is_configuring) { 1394 if (is_configuring) {
1260 tmp_is_connected = true; 1395 tmp_is_connected = true;
1261 lock.unlock();
1262 TriggerOnChange(ControllerTriggerType::Connected, false);
1263 return; 1396 return;
1264 } 1397 }
1265 1398
1266 if (is_connected) { 1399 if (is_connected) {
1400 trigger_guard.Cancel();
1267 return; 1401 return;
1268 } 1402 }
1269 is_connected = true; 1403 is_connected = true;
1270
1271 lock.unlock();
1272 TriggerOnChange(ControllerTriggerType::Connected, true);
1273} 1404}
1274 1405
1275void EmulatedController::Disconnect() { 1406void EmulatedController::Disconnect() {
1276 std::unique_lock lock{mutex}; 1407 auto trigger_guard =
1408 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
1409 std::scoped_lock lock{mutex};
1277 if (is_configuring) { 1410 if (is_configuring) {
1278 tmp_is_connected = false; 1411 tmp_is_connected = false;
1279 lock.unlock();
1280 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1281 return; 1412 return;
1282 } 1413 }
1283 1414
1284 if (!is_connected) { 1415 if (!is_connected) {
1416 trigger_guard.Cancel();
1285 return; 1417 return;
1286 } 1418 }
1287 is_connected = false; 1419 is_connected = false;
1288
1289 lock.unlock();
1290 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1291} 1420}
1292 1421
1293bool EmulatedController::IsConnected(bool get_temporary_value) const { 1422bool EmulatedController::IsConnected(bool get_temporary_value) const {
@@ -1312,19 +1441,21 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1312} 1441}
1313 1442
1314void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1443void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1315 std::unique_lock lock{mutex}; 1444 auto trigger_guard =
1445 SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
1446 std::scoped_lock lock{mutex};
1316 1447
1317 if (is_configuring) { 1448 if (is_configuring) {
1318 if (tmp_npad_type == npad_type_) { 1449 if (tmp_npad_type == npad_type_) {
1450 trigger_guard.Cancel();
1319 return; 1451 return;
1320 } 1452 }
1321 tmp_npad_type = npad_type_; 1453 tmp_npad_type = npad_type_;
1322 lock.unlock();
1323 TriggerOnChange(ControllerTriggerType::Type, false);
1324 return; 1454 return;
1325 } 1455 }
1326 1456
1327 if (npad_type == npad_type_) { 1457 if (npad_type == npad_type_) {
1458 trigger_guard.Cancel();
1328 return; 1459 return;
1329 } 1460 }
1330 if (is_connected) { 1461 if (is_connected) {
@@ -1332,9 +1463,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1332 NpadIdTypeToIndex(npad_id_type)); 1463 NpadIdTypeToIndex(npad_id_type));
1333 } 1464 }
1334 npad_type = npad_type_; 1465 npad_type = npad_type_;
1335
1336 lock.unlock();
1337 TriggerOnChange(ControllerTriggerType::Type, true);
1338} 1466}
1339 1467
1340LedPattern EmulatedController::GetLedPattern() const { 1468LedPattern EmulatedController::GetLedPattern() const {
@@ -1395,6 +1523,10 @@ CameraValues EmulatedController::GetCameraValues() const {
1395 return controller.camera_values; 1523 return controller.camera_values;
1396} 1524}
1397 1525
1526RingAnalogValue EmulatedController::GetRingSensorValues() const {
1527 return controller.ring_analog_value;
1528}
1529
1398HomeButtonState EmulatedController::GetHomeButtons() const { 1530HomeButtonState EmulatedController::GetHomeButtons() const {
1399 std::scoped_lock lock{mutex}; 1531 std::scoped_lock lock{mutex};
1400 if (is_configuring) { 1532 if (is_configuring) {
@@ -1428,7 +1560,7 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1428} 1560}
1429 1561
1430AnalogSticks EmulatedController::GetSticks() const { 1562AnalogSticks EmulatedController::GetSticks() const {
1431 std::unique_lock lock{mutex}; 1563 std::scoped_lock lock{mutex};
1432 1564
1433 if (is_configuring) { 1565 if (is_configuring) {
1434 return {}; 1566 return {};
@@ -1478,6 +1610,10 @@ const CameraState& EmulatedController::GetCamera() const {
1478 return controller.camera_state; 1610 return controller.camera_state;
1479} 1611}
1480 1612
1613RingSensorForce EmulatedController::GetRingSensorForce() const {
1614 return controller.ring_analog_state;
1615}
1616
1481const NfcState& EmulatedController::GetNfc() const { 1617const NfcState& EmulatedController::GetNfc() const {
1482 std::scoped_lock lock{mutex}; 1618 std::scoped_lock lock{mutex};
1483 return controller.nfc_state; 1619 return controller.nfc_state;
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index a398543a6..3ac77b2b5 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -35,19 +35,27 @@ using ControllerMotionDevices =
35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>; 35 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeMotion::NumMotions>;
36using TriggerDevices = 36using TriggerDevices =
37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>; 37 std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
38using ColorDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
38using BatteryDevices = 40using BatteryDevices =
39 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; 41 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
40using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; 42using CameraDevices =
41using NfcDevices = std::unique_ptr<Common::Input::InputDevice>; 43 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
44using RingAnalogDevices =
45 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
46using NfcDevices =
47 std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
42using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>; 48using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
43 49
44using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>; 50using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
45using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>; 51using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
46using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>; 52using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
47using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>; 53using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
54using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;
48using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; 55using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
49using CameraParams = Common::ParamPackage; 56using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;
50using NfcParams = Common::ParamPackage; 57using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;
58using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;
51using OutputParams = std::array<Common::ParamPackage, output_devices_size>; 59using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
52 60
53using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>; 61using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
@@ -58,6 +66,7 @@ using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::Native
58using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>; 66using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
59using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>; 67using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
60using CameraValues = Common::Input::CameraStatus; 68using CameraValues = Common::Input::CameraStatus;
69using RingAnalogValue = Common::Input::AnalogStatus;
61using NfcValues = Common::Input::NfcStatus; 70using NfcValues = Common::Input::NfcStatus;
62using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>; 71using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
63 72
@@ -84,6 +93,10 @@ struct CameraState {
84 std::size_t sample{}; 93 std::size_t sample{};
85}; 94};
86 95
96struct RingSensorForce {
97 f32 force;
98};
99
87struct NfcState { 100struct NfcState {
88 Common::Input::NfcState state{}; 101 Common::Input::NfcState state{};
89 std::vector<u8> data{}; 102 std::vector<u8> data{};
@@ -116,6 +129,7 @@ struct ControllerStatus {
116 BatteryValues battery_values{}; 129 BatteryValues battery_values{};
117 VibrationValues vibration_values{}; 130 VibrationValues vibration_values{};
118 CameraValues camera_values{}; 131 CameraValues camera_values{};
132 RingAnalogValue ring_analog_value{};
119 NfcValues nfc_values{}; 133 NfcValues nfc_values{};
120 134
121 // Data for HID serices 135 // Data for HID serices
@@ -129,6 +143,7 @@ struct ControllerStatus {
129 ControllerColors colors_state{}; 143 ControllerColors colors_state{};
130 BatteryLevelState battery_state{}; 144 BatteryLevelState battery_state{};
131 CameraState camera_state{}; 145 CameraState camera_state{};
146 RingSensorForce ring_analog_state{};
132 NfcState nfc_state{}; 147 NfcState nfc_state{};
133}; 148};
134 149
@@ -141,6 +156,7 @@ enum class ControllerTriggerType {
141 Battery, 156 Battery,
142 Vibration, 157 Vibration,
143 IrSensor, 158 IrSensor,
159 RingController,
144 Nfc, 160 Nfc,
145 Connected, 161 Connected,
146 Disconnected, 162 Disconnected,
@@ -294,6 +310,9 @@ public:
294 /// Returns the latest camera status from the controller with parameters 310 /// Returns the latest camera status from the controller with parameters
295 CameraValues GetCameraValues() const; 311 CameraValues GetCameraValues() const;
296 312
313 /// Returns the latest status of analog input from the ring sensor with parameters
314 RingAnalogValue GetRingSensorValues() const;
315
297 /// Returns the latest status of button input for the hid::HomeButton service 316 /// Returns the latest status of button input for the hid::HomeButton service
298 HomeButtonState GetHomeButtons() const; 317 HomeButtonState GetHomeButtons() const;
299 318
@@ -324,6 +343,9 @@ public:
324 /// Returns the latest camera status from the controller 343 /// Returns the latest camera status from the controller
325 const CameraState& GetCamera() const; 344 const CameraState& GetCamera() const;
326 345
346 /// Returns the latest ringcon force sensor value
347 RingSensorForce GetRingSensorForce() const;
348
327 /// Returns the latest ntag status from the controller 349 /// Returns the latest ntag status from the controller
328 const NfcState& GetNfc() const; 350 const NfcState& GetNfc() const;
329 351
@@ -341,10 +363,12 @@ public:
341 363
342 /** 364 /**
343 * Sets the desired data to be polled from a controller 365 * Sets the desired data to be polled from a controller
366 * @param device_index index of the controller to set the polling mode
344 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc. 367 * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
345 * @return true if SetPollingMode was successfull 368 * @return driver result from this command
346 */ 369 */
347 bool SetPollingMode(Common::Input::PollingMode polling_mode); 370 Common::Input::DriverResult SetPollingMode(EmulatedDeviceIndex device_index,
371 Common::Input::PollingMode polling_mode);
348 372
349 /** 373 /**
350 * Sets the desired camera format to be polled from a controller 374 * Sets the desired camera format to be polled from a controller
@@ -353,6 +377,15 @@ public:
353 */ 377 */
354 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format); 378 bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
355 379
380 // Returns the current mapped ring device
381 Common::ParamPackage GetRingParam() const;
382
383 /**
384 * Updates the current mapped ring device
385 * @param param ParamPackage with ring sensor data to be mapped
386 */
387 void SetRingParam(Common::ParamPackage param);
388
356 /// Returns true if the device has nfc support 389 /// Returns true if the device has nfc support
357 bool HasNfc() const; 390 bool HasNfc() const;
358 391
@@ -433,9 +466,16 @@ private:
433 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index); 466 void SetMotion(const Common::Input::CallbackStatus& callback, std::size_t index);
434 467
435 /** 468 /**
469 * Updates the color status of the controller
470 * @param callback A CallbackStatus containing the color status
471 * @param index color ID of the to be updated
472 */
473 void SetColors(const Common::Input::CallbackStatus& callback, std::size_t index);
474
475 /**
436 * Updates the battery status of the controller 476 * Updates the battery status of the controller
437 * @param callback A CallbackStatus containing the battery status 477 * @param callback A CallbackStatus containing the battery status
438 * @param index Button ID of the to be updated 478 * @param index battery ID of the to be updated
439 */ 479 */
440 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index); 480 void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
441 481
@@ -446,6 +486,12 @@ private:
446 void SetCamera(const Common::Input::CallbackStatus& callback); 486 void SetCamera(const Common::Input::CallbackStatus& callback);
447 487
448 /** 488 /**
489 * Updates the ring analog sensor status of the ring controller
490 * @param callback A CallbackStatus containing the force status
491 */
492 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
493
494 /**
449 * Updates the nfc status of the controller 495 * Updates the nfc status of the controller
450 * @param callback A CallbackStatus containing the nfc status 496 * @param callback A CallbackStatus containing the nfc status
451 */ 497 */
@@ -484,7 +530,9 @@ private:
484 ControllerMotionParams motion_params; 530 ControllerMotionParams motion_params;
485 TriggerParams trigger_params; 531 TriggerParams trigger_params;
486 BatteryParams battery_params; 532 BatteryParams battery_params;
533 ColorParams color_params;
487 CameraParams camera_params; 534 CameraParams camera_params;
535 RingAnalogParams ring_params;
488 NfcParams nfc_params; 536 NfcParams nfc_params;
489 OutputParams output_params; 537 OutputParams output_params;
490 538
@@ -493,7 +541,9 @@ private:
493 ControllerMotionDevices motion_devices; 541 ControllerMotionDevices motion_devices;
494 TriggerDevices trigger_devices; 542 TriggerDevices trigger_devices;
495 BatteryDevices battery_devices; 543 BatteryDevices battery_devices;
544 ColorDevices color_devices;
496 CameraDevices camera_devices; 545 CameraDevices camera_devices;
546 RingAnalogDevices ring_analog_devices;
497 NfcDevices nfc_devices; 547 NfcDevices nfc_devices;
498 OutputDevices output_devices; 548 OutputDevices output_devices;
499 549
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index e421828d2..836f32c0f 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -14,7 +14,6 @@ EmulatedDevices::EmulatedDevices() = default;
14EmulatedDevices::~EmulatedDevices() = default; 14EmulatedDevices::~EmulatedDevices() = default;
15 15
16void EmulatedDevices::ReloadFromSettings() { 16void EmulatedDevices::ReloadFromSettings() {
17 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 17 ReloadInput();
19} 18}
20 19
@@ -66,8 +65,6 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 65 key_index++;
67 } 66 }
68 67
69 ring_analog_device = Common::Input::CreateInputDevice(ring_params);
70
71 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 68 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
72 if (!mouse_button_devices[index]) { 69 if (!mouse_button_devices[index]) {
73 continue; 70 continue;
@@ -122,13 +119,6 @@ void EmulatedDevices::ReloadInput() {
122 }, 119 },
123 }); 120 });
124 } 121 }
125
126 if (ring_analog_device) {
127 ring_analog_device->SetCallback({
128 .on_change =
129 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
130 });
131 }
132} 122}
133 123
134void EmulatedDevices::UnloadInput() { 124void EmulatedDevices::UnloadInput() {
@@ -145,7 +135,6 @@ void EmulatedDevices::UnloadInput() {
145 for (auto& button : keyboard_modifier_devices) { 135 for (auto& button : keyboard_modifier_devices) {
146 button.reset(); 136 button.reset();
147 } 137 }
148 ring_analog_device.reset();
149} 138}
150 139
151void EmulatedDevices::EnableConfiguration() { 140void EmulatedDevices::EnableConfiguration() {
@@ -165,7 +154,6 @@ void EmulatedDevices::SaveCurrentConfig() {
165 if (!is_configuring) { 154 if (!is_configuring) {
166 return; 155 return;
167 } 156 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
169} 157}
170 158
171void EmulatedDevices::RestoreConfig() { 159void EmulatedDevices::RestoreConfig() {
@@ -175,15 +163,6 @@ void EmulatedDevices::RestoreConfig() {
175 ReloadFromSettings(); 163 ReloadFromSettings();
176} 164}
177 165
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 166void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
188 std::size_t index) { 167 std::size_t index) {
189 if (index >= device_status.keyboard_values.size()) { 168 if (index >= device_status.keyboard_values.size()) {
@@ -430,23 +409,6 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
430 TriggerOnChange(DeviceTriggerType::Mouse); 409 TriggerOnChange(DeviceTriggerType::Mouse);
431} 410}
432 411
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
450KeyboardValues EmulatedDevices::GetKeyboardValues() const { 412KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex}; 413 std::scoped_lock lock{mutex};
452 return device_status.keyboard_values; 414 return device_status.keyboard_values;
@@ -462,10 +424,6 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
462 return device_status.mouse_button_values; 424 return device_status.mouse_button_values;
463} 425}
464 426
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
469KeyboardKey EmulatedDevices::GetKeyboard() const { 427KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex}; 428 std::scoped_lock lock{mutex};
471 return device_status.keyboard_state; 429 return device_status.keyboard_state;
@@ -491,10 +449,6 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
491 return device_status.mouse_wheel_state; 449 return device_status.mouse_wheel_state;
492} 450}
493 451
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 452void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex}; 453 std::scoped_lock lock{callback_mutex};
500 for (const auto& poller_pair : callback_list) { 454 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 4cdbf9dc6..76f9150df 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,11 +26,9 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
30 29
31using MouseButtonParams = 30using MouseButtonParams =
32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
34 32
35using KeyboardValues = 33using KeyboardValues =
36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -41,17 +39,12 @@ using MouseButtonValues =
41using MouseAnalogValues = 39using MouseAnalogValues =
42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
43using MouseStickValue = Common::Input::TouchStatus; 41using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
45 42
46struct MousePosition { 43struct MousePosition {
47 f32 x; 44 f32 x;
48 f32 y; 45 f32 y;
49}; 46};
50 47
51struct RingSensorForce {
52 f32 force;
53};
54
55struct DeviceStatus { 48struct DeviceStatus {
56 // Data from input_common 49 // Data from input_common
57 KeyboardValues keyboard_values{}; 50 KeyboardValues keyboard_values{};
@@ -59,7 +52,6 @@ struct DeviceStatus {
59 MouseButtonValues mouse_button_values{}; 52 MouseButtonValues mouse_button_values{};
60 MouseAnalogValues mouse_analog_values{}; 53 MouseAnalogValues mouse_analog_values{};
61 MouseStickValue mouse_stick_value{}; 54 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
63 55
64 // Data for HID serices 56 // Data for HID serices
65 KeyboardKey keyboard_state{}; 57 KeyboardKey keyboard_state{};
@@ -67,7 +59,6 @@ struct DeviceStatus {
67 MouseButton mouse_button_state{}; 59 MouseButton mouse_button_state{};
68 MousePosition mouse_position_state{}; 60 MousePosition mouse_position_state{};
69 AnalogStickState mouse_wheel_state{}; 61 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
71}; 62};
72 63
73enum class DeviceTriggerType { 64enum class DeviceTriggerType {
@@ -138,9 +129,6 @@ public:
138 /// Returns the latest status of button input from the mouse with parameters 129 /// Returns the latest status of button input from the mouse with parameters
139 MouseButtonValues GetMouseButtonsValues() const; 130 MouseButtonValues GetMouseButtonsValues() const;
140 131
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
144 /// Returns the latest status of button input from the keyboard 132 /// Returns the latest status of button input from the keyboard
145 KeyboardKey GetKeyboard() const; 133 KeyboardKey GetKeyboard() const;
146 134
@@ -156,9 +144,6 @@ public:
156 /// Returns the latest mouse wheel change 144 /// Returns the latest mouse wheel change
157 AnalogStickState GetMouseWheel() const; 145 AnalogStickState GetMouseWheel() const;
158 146
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
162 /** 147 /**
163 * Adds a callback to the list of events 148 * Adds a callback to the list of events
164 * @param update_callback InterfaceUpdateCallback that will be triggered 149 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -224,14 +209,11 @@ private:
224 209
225 bool is_configuring{false}; 210 bool is_configuring{false};
226 211
227 RingAnalogParams ring_params;
228
229 KeyboardDevices keyboard_devices; 212 KeyboardDevices keyboard_devices;
230 KeyboardModifierDevices keyboard_modifier_devices; 213 KeyboardModifierDevices keyboard_modifier_devices;
231 MouseButtonDevices mouse_button_devices; 214 MouseButtonDevices mouse_button_devices;
232 MouseAnalogDevices mouse_analog_devices; 215 MouseAnalogDevices mouse_analog_devices;
233 MouseStickDevice mouse_stick_device; 216 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
235 217
236 mutable std::mutex mutex; 218 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex; 219 mutable std::mutex callback_mutex;
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 502692875..3f7b8c090 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -304,6 +304,18 @@ Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& cal
304 return nfc; 304 return nfc;
305} 305}
306 306
307Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback) {
308 switch (callback.type) {
309 case Common::Input::InputType::Color:
310 return callback.color_status;
311 break;
312 default:
313 LOG_ERROR(Input, "Conversion from type {} to color not implemented", callback.type);
314 return {};
315 break;
316 }
317}
318
307void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) { 319void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
308 const auto& properties = analog.properties; 320 const auto& properties = analog.properties;
309 float& raw_value = analog.raw_value; 321 float& raw_value = analog.raw_value;
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index b7eb6e660..c51c03e57 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -88,11 +88,19 @@ Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatu
88 * Converts raw input data into a valid nfc status. 88 * Converts raw input data into a valid nfc status.
89 * 89 *
90 * @param callback Supported callbacks: Nfc. 90 * @param callback Supported callbacks: Nfc.
91 * @return A valid CameraObject object. 91 * @return A valid data tag vector.
92 */ 92 */
93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback); 93Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
94 94
95/** 95/**
96 * Converts raw input data into a valid color status.
97 *
98 * @param callback Supported callbacks: Color.
99 * @return A valid Color object.
100 */
101Common::Input::BodyColorStatus TransformToColor(const Common::Input::CallbackStatus& callback);
102
103/**
96 * Converts raw analog data into a valid analog value 104 * Converts raw analog data into a valid analog value
97 * @param analog An analog object containing raw data and properties 105 * @param analog An analog object containing raw data and properties
98 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f. 106 * @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index d9da1e600..884eba001 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -74,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {
74 R_UNLESS(!m_is_mapped, ResultInvalidState); 74 R_UNLESS(!m_is_mapped, ResultInvalidState);
75 75
76 // Map the memory. 76 // Map the memory.
77 R_TRY(kernel.CurrentProcess()->PageTable().MapPages( 77 R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup(
78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); 78 address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
79 79
80 // Mark ourselves as mapped. 80 // Mark ourselves as mapped.
@@ -91,8 +91,8 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {
91 KScopedLightLock lk(m_lock); 91 KScopedLightLock lk(m_lock);
92 92
93 // Unmap the memory. 93 // Unmap the memory.
94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, 94 R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group,
95 KMemoryState::CodeOut)); 95 KMemoryState::CodeOut));
96 96
97 // Mark ourselves as unmapped. 97 // Mark ourselves as unmapped.
98 m_is_mapped = false; 98 m_is_mapped = false;
@@ -125,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission
125 } 125 }
126 126
127 // Map the memory. 127 // Map the memory.
128 R_TRY( 128 R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode,
129 m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); 129 k_perm));
130 130
131 // Mark ourselves as mapped. 131 // Mark ourselves as mapped.
132 m_is_owner_mapped = true; 132 m_is_owner_mapped = true;
@@ -142,7 +142,7 @@ Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
142 KScopedLightLock lk(m_lock); 142 KScopedLightLock lk(m_lock);
143 143
144 // Unmap the memory. 144 // Unmap the memory.
145 R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); 145 R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));
146 146
147 // Mark ourselves as unmapped. 147 // Mark ourselves as unmapped.
148 m_is_owner_mapped = false; 148 m_is_owner_mapped = false;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index 124149697..0c6b20db3 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -171,7 +171,7 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value)
171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); 171 R_UNLESS(owner_thread != nullptr, ResultInvalidHandle);
172 172
173 // Update the lock. 173 // Update the lock.
174 cur_thread->SetAddressKey(addr, value); 174 cur_thread->SetUserAddressKey(addr, value);
175 owner_thread->AddWaiter(cur_thread); 175 owner_thread->AddWaiter(cur_thread);
176 176
177 // Begin waiting. 177 // Begin waiting.
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 43185320d..d791acbe3 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -68,7 +68,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) {
68 68
69 // Add the current thread as a waiter on the owner. 69 // Add the current thread as a waiter on the owner.
70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); 70 KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL);
71 cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); 71 cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag)));
72 owner_thread->AddWaiter(cur_thread); 72 owner_thread->AddWaiter(cur_thread);
73 73
74 // Begin waiting to hold the lock. 74 // Begin waiting to hold the lock.
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index fd6e1d3e6..17fa1a6ed 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -67,9 +67,9 @@ constexpr size_t KernelPageBufferAdditionalSize = 0x33C000;
67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + 67constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize +
68 KernelSlabHeapSize + KernelPageBufferHeapSize; 68 KernelSlabHeapSize + KernelPageBufferHeapSize;
69 69
70constexpr bool IsKernelAddressKey(VAddr key) { 70//! NB: Use KThread::GetAddressKeyIsKernel().
71 return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; 71//! See explanation for deviation of GetAddressKey.
72} 72bool IsKernelAddressKey(VAddr key) = delete;
73 73
74constexpr bool IsKernelAddress(VAddr address) { 74constexpr bool IsKernelAddress(VAddr address) {
75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; 75 return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd;
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9c7ac22dc..2e13d5d0d 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -435,6 +435,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
435 KPageGroup pg{m_kernel, m_block_info_manager}; 435 KPageGroup pg{m_kernel, m_block_info_manager};
436 AddRegionToPages(src_address, num_pages, pg); 436 AddRegionToPages(src_address, num_pages, pg);
437 437
438 // We're going to perform an update, so create a helper.
439 KScopedPageTableUpdater updater(this);
440
438 // Reprotect the source as kernel-read/not mapped. 441 // Reprotect the source as kernel-read/not mapped.
439 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | 442 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
440 KMemoryPermission::NotMapped); 443 KMemoryPermission::NotMapped);
@@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si
447 }); 450 });
448 451
449 // Map the alias pages. 452 // Map the alias pages.
450 R_TRY(MapPages(dst_address, pg, new_perm)); 453 const KPageProperties dst_properties = {new_perm, false, false,
454 DisableMergeAttribute::DisableHead};
455 R_TRY(
456 this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));
451 457
452 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on 458 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
453 // failure. 459 // failure.
@@ -1881,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {
1881 R_SUCCEED(); 1887 R_SUCCEED();
1882} 1888}
1883 1889
1884Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1890Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1891 size_t size) {
1885 // Lock the table. 1892 // Lock the table.
1886 KScopedLightLock lk(m_general_lock); 1893 KScopedLightLock lk(m_general_lock);
1887 1894
@@ -1902,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)
1902 KMemoryAttribute::None)); 1909 KMemoryAttribute::None));
1903 1910
1904 // Create an update allocator for the source. 1911 // Create an update allocator for the source.
1905 Result src_allocator_result{ResultSuccess}; 1912 Result src_allocator_result;
1906 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 1913 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1907 m_memory_block_slab_manager, 1914 m_memory_block_slab_manager,
1908 num_src_allocator_blocks); 1915 num_src_allocator_blocks);
1909 R_TRY(src_allocator_result); 1916 R_TRY(src_allocator_result);
1910 1917
1911 // Create an update allocator for the destination. 1918 // Create an update allocator for the destination.
1912 Result dst_allocator_result{ResultSuccess}; 1919 Result dst_allocator_result;
1913 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 1920 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1914 m_memory_block_slab_manager, 1921 m_memory_block_slab_manager,
1915 num_dst_allocator_blocks); 1922 num_dst_allocator_blocks);
1916 R_TRY(dst_allocator_result); 1923 R_TRY(dst_allocator_result);
1917 1924
1918 // Map the memory. 1925 // Map the memory.
1919 KPageGroup page_linked_list{m_kernel, m_block_info_manager};
1920 const size_t num_pages{size / PageSize};
1921 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1922 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1923 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1924
1925 AddRegionToPages(src_address, num_pages, page_linked_list);
1926 { 1926 {
1927 // Determine the number of pages being operated on.
1928 const size_t num_pages = size / PageSize;
1929
1930 // Create page groups for the memory being unmapped.
1931 KPageGroup pg{m_kernel, m_block_info_manager};
1932
1933 // Create the page group representing the source.
1934 R_TRY(this->MakePageGroup(pg, src_address, num_pages));
1935
1936 // We're going to perform an update, so create a helper.
1937 KScopedPageTableUpdater updater(this);
1938
1927 // Reprotect the source as kernel-read/not mapped. 1939 // Reprotect the source as kernel-read/not mapped.
1928 auto block_guard = detail::ScopeExit([&] { 1940 const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>(
1929 Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 1941 KMemoryPermission::KernelRead | KMemoryPermission::NotMapped);
1930 OperationType::ChangePermissions); 1942 const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked;
1931 }); 1943 const KPageProperties src_properties = {new_src_perm, false, false,
1932 R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); 1944 DisableMergeAttribute::DisableHeadBodyTail};
1933 R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); 1945 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
1946 OperationType::ChangePermissions));
1934 1947
1935 block_guard.Cancel(); 1948 // Ensure that we unprotect the source pages on failure.
1936 } 1949 ON_RESULT_FAILURE {
1950 const KPageProperties unprotect_properties = {
1951 KMemoryPermission::UserReadWrite, false, false,
1952 DisableMergeAttribute::EnableHeadBodyTail};
1953 ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm,
1954 OperationType::ChangePermissions) == ResultSuccess);
1955 };
1937 1956
1938 // Apply the memory block updates. 1957 // Map the alias pages.
1939 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 1958 const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false,
1940 new_src_perm, new_src_attr, 1959 DisableMergeAttribute::DisableHead};
1941 KMemoryBlockDisableMergeAttribute::Locked, 1960 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties,
1942 KMemoryBlockDisableMergeAttribute::None); 1961 false));
1943 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 1962
1944 KMemoryState::Stack, KMemoryPermission::UserReadWrite, 1963 // Apply the memory block updates.
1945 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, 1964 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages,
1946 KMemoryBlockDisableMergeAttribute::None); 1965 src_state, new_src_perm, new_src_attr,
1966 KMemoryBlockDisableMergeAttribute::Locked,
1967 KMemoryBlockDisableMergeAttribute::None);
1968 m_memory_block_manager.Update(
1969 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack,
1970 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
1971 KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None);
1972 }
1947 1973
1948 R_SUCCEED(); 1974 R_SUCCEED();
1949} 1975}
1950 1976
1951Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { 1977Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address,
1978 size_t size) {
1952 // Lock the table. 1979 // Lock the table.
1953 KScopedLightLock lk(m_general_lock); 1980 KScopedLightLock lk(m_general_lock);
1954 1981
@@ -1970,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size
1970 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); 1997 KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));
1971 1998
1972 // Create an update allocator for the source. 1999 // Create an update allocator for the source.
1973 Result src_allocator_result{ResultSuccess}; 2000 Result src_allocator_result;
1974 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), 2001 KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),
1975 m_memory_block_slab_manager, 2002 m_memory_block_slab_manager,
1976 num_src_allocator_blocks); 2003 num_src_allocator_blocks);
1977 R_TRY(src_allocator_result); 2004 R_TRY(src_allocator_result);
1978 2005
1979 // Create an update allocator for the destination. 2006 // Create an update allocator for the destination.
1980 Result dst_allocator_result{ResultSuccess}; 2007 Result dst_allocator_result;
1981 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), 2008 KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),
1982 m_memory_block_slab_manager, 2009 m_memory_block_slab_manager,
1983 num_dst_allocator_blocks); 2010 num_dst_allocator_blocks);
1984 R_TRY(dst_allocator_result); 2011 R_TRY(dst_allocator_result);
1985 2012
1986 KPageGroup src_pages{m_kernel, m_block_info_manager}; 2013 // Unmap the memory.
1987 KPageGroup dst_pages{m_kernel, m_block_info_manager}; 2014 {
1988 const size_t num_pages{size / PageSize}; 2015 // Determine the number of pages being operated on.
2016 const size_t num_pages = size / PageSize;
1989 2017
1990 AddRegionToPages(src_address, num_pages, src_pages); 2018 // Create page groups for the memory being unmapped.
1991 AddRegionToPages(dst_address, num_pages, dst_pages); 2019 KPageGroup pg{m_kernel, m_block_info_manager};
1992 2020
1993 R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); 2021 // Create the page group representing the destination.
2022 R_TRY(this->MakePageGroup(pg, dst_address, num_pages));
1994 2023
1995 { 2024 // Ensure the page group is the valid for the source.
1996 auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); 2025 R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion);
1997 2026
1998 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2027 // We're going to perform an update, so create a helper.
1999 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, 2028 KScopedPageTableUpdater updater(this);
2000 OperationType::ChangePermissions));
2001 2029
2002 block_guard.Cancel(); 2030 // Unmap the aliased copy of the pages.
2003 } 2031 const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false,
2032 DisableMergeAttribute::None};
2033 R_TRY(
2034 this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap));
2035
2036 // Ensure that we re-map the aliased pages on failure.
2037 ON_RESULT_FAILURE {
2038 this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg);
2039 };
2004 2040
2005 // Apply the memory block updates. 2041 // Try to set the permissions for the source pages back to what they should be.
2006 m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, 2042 const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false,
2007 KMemoryPermission::UserReadWrite, KMemoryAttribute::None, 2043 DisableMergeAttribute::EnableAndMergeHeadBodyTail};
2008 KMemoryBlockDisableMergeAttribute::None, 2044 R_TRY(this->Operate(src_address, num_pages, src_properties.perm,
2009 KMemoryBlockDisableMergeAttribute::Locked); 2045 OperationType::ChangePermissions));
2010 m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, 2046
2011 KMemoryState::None, KMemoryPermission::None, 2047 // Apply the memory block updates.
2012 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, 2048 m_memory_block_manager.Update(
2013 KMemoryBlockDisableMergeAttribute::Normal); 2049 std::addressof(src_allocator), src_address, num_pages, src_state,
2050 KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
2051 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked);
2052 m_memory_block_manager.Update(
2053 std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None,
2054 KMemoryPermission::None, KMemoryAttribute::None,
2055 KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal);
2056 }
2014 2057
2015 R_SUCCEED(); 2058 R_SUCCEED();
2016} 2059}
2017 2060
2018Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, 2061Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
2019 KMemoryPermission perm) { 2062 size_t num_pages, KMemoryPermission perm) {
2020 ASSERT(this->IsLockedByCurrentThread()); 2063 ASSERT(this->IsLockedByCurrentThread());
2021 2064
2022 VAddr cur_addr{addr}; 2065 // Create a page group to hold the pages we allocate.
2066 KPageGroup pg{m_kernel, m_block_info_manager};
2023 2067
2024 for (const auto& node : page_linked_list) { 2068 // Allocate the pages.
2025 if (const auto result{ 2069 R_TRY(
2026 Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; 2070 m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option));
2027 result.IsError()) {
2028 const size_t num_pages{(addr - cur_addr) / PageSize};
2029 2071
2030 ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) 2072 // Ensure that the page group is closed when we're done working with it.
2031 .IsSuccess()); 2073 SCOPE_EXIT({ pg.Close(); });
2032 2074
2033 R_RETURN(result); 2075 // Clear all pages.
2076 for (const auto& it : pg) {
2077 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value,
2078 it.GetSize());
2079 }
2080
2081 // Map the pages.
2082 R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup));
2083}
2084
2085Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
2086 const KPageGroup& pg, const KPageProperties properties,
2087 bool reuse_ll) {
2088 ASSERT(this->IsLockedByCurrentThread());
2089
2090 // Note the current address, so that we can iterate.
2091 const KProcessAddress start_address = address;
2092 KProcessAddress cur_address = address;
2093
2094 // Ensure that we clean up on failure.
2095 ON_RESULT_FAILURE {
2096 ASSERT(!reuse_ll);
2097 if (cur_address != start_address) {
2098 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2099 DisableMergeAttribute::None};
2100 ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize,
2101 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);
2034 } 2102 }
2103 };
2035 2104
2036 cur_addr += node.GetNumPages() * PageSize; 2105 // Iterate, mapping all pages in the group.
2106 for (const auto& block : pg) {
2107 // Map and advance.
2108 const KPageProperties cur_properties =
2109 (cur_address == start_address)
2110 ? properties
2111 : KPageProperties{properties.perm, properties.io, properties.uncached,
2112 DisableMergeAttribute::None};
2113 this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map,
2114 block.GetAddress());
2115 cur_address += block.GetSize();
2037 } 2116 }
2038 2117
2118 // We succeeded!
2039 R_SUCCEED(); 2119 R_SUCCEED();
2040} 2120}
2041 2121
2042Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, 2122void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
2043 KMemoryPermission perm) { 2123 const KPageGroup& pg) {
2044 // Check that the map is in range. 2124 ASSERT(this->IsLockedByCurrentThread());
2045 const size_t num_pages{page_linked_list.GetNumPages()};
2046 const size_t size{num_pages * PageSize};
2047 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2048 2125
2049 // Lock the table. 2126 // Note the current address, so that we can iterate.
2050 KScopedLightLock lk(m_general_lock); 2127 const KProcessAddress start_address = address;
2128 const KProcessAddress last_address = start_address + size - 1;
2129 const KProcessAddress end_address = last_address + 1;
2051 2130
2052 // Check the memory state. 2131 // Iterate over the memory.
2053 R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, 2132 auto pg_it = pg.begin();
2054 KMemoryPermission::None, KMemoryPermission::None, 2133 ASSERT(pg_it != pg.end());
2055 KMemoryAttribute::None, KMemoryAttribute::None));
2056 2134
2057 // Create an update allocator. 2135 KPhysicalAddress pg_phys_addr = pg_it->GetAddress();
2058 Result allocator_result{ResultSuccess}; 2136 size_t pg_pages = pg_it->GetNumPages();
2059 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2060 m_memory_block_slab_manager);
2061 2137
2062 // Map the pages. 2138 auto it = m_memory_block_manager.FindIterator(start_address);
2063 R_TRY(MapPages(address, page_linked_list, perm)); 2139 while (true) {
2140 // Check that the iterator is valid.
2141 ASSERT(it != m_memory_block_manager.end());
2064 2142
2065 // Update the blocks. 2143 // Get the memory info.
2066 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, 2144 const KMemoryInfo info = it->GetMemoryInfo();
2067 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2068 KMemoryBlockDisableMergeAttribute::None);
2069 2145
2070 R_SUCCEED(); 2146 // Determine the range to map.
2147 KProcessAddress map_address = std::max<VAddr>(info.GetAddress(), start_address);
2148 const KProcessAddress map_end_address = std::min<VAddr>(info.GetEndAddress(), end_address);
2149 ASSERT(map_end_address != map_address);
2150
2151 // Determine if we should disable head merge.
2152 const bool disable_head_merge =
2153 info.GetAddress() >= start_address &&
2154 True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal);
2155 const KPageProperties map_properties = {
2156 info.GetPermission(), false, false,
2157 disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None};
2158
2159 // While we have pages to map, map them.
2160 size_t map_pages = (map_end_address - map_address) / PageSize;
2161 while (map_pages > 0) {
2162 // Check if we're at the end of the physical block.
2163 if (pg_pages == 0) {
2164 // Ensure there are more pages to map.
2165 ASSERT(pg_it != pg.end());
2166
2167 // Advance our physical block.
2168 ++pg_it;
2169 pg_phys_addr = pg_it->GetAddress();
2170 pg_pages = pg_it->GetNumPages();
2171 }
2172
2173 // Map whatever we can.
2174 const size_t cur_pages = std::min(pg_pages, map_pages);
2175 ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map,
2176 pg_phys_addr) == ResultSuccess);
2177
2178 // Advance.
2179 map_address += cur_pages * PageSize;
2180 map_pages -= cur_pages;
2181
2182 pg_phys_addr += cur_pages * PageSize;
2183 pg_pages -= cur_pages;
2184 }
2185
2186 // Check if we're done.
2187 if (last_address <= info.GetLastAddress()) {
2188 break;
2189 }
2190
2191 // Advance.
2192 ++it;
2193 }
2194
2195 // Check that we re-mapped precisely the page group.
2196 ASSERT((++pg_it) == pg.end());
2071} 2197}
2072 2198
2073Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 2199Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
2074 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 2200 KPhysicalAddress phys_addr, bool is_pa_valid,
2201 KProcessAddress region_start, size_t region_num_pages,
2075 KMemoryState state, KMemoryPermission perm) { 2202 KMemoryState state, KMemoryPermission perm) {
2076 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); 2203 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
2077 2204
@@ -2084,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2084 KScopedLightLock lk(m_general_lock); 2211 KScopedLightLock lk(m_general_lock);
2085 2212
2086 // Find a random address to map at. 2213 // Find a random address to map at.
2087 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, 2214 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment,
2088 this->GetNumGuardPages()); 2215 0, this->GetNumGuardPages());
2089 R_UNLESS(addr != 0, ResultOutOfMemory); 2216 R_UNLESS(addr != 0, ResultOutOfMemory);
2090 ASSERT(Common::IsAligned(addr, alignment)); 2217 ASSERT(Common::IsAligned(addr, alignment));
2091 ASSERT(this->CanContain(addr, num_pages * PageSize, state)); 2218 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2092 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, 2219 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2093 KMemoryPermission::None, KMemoryPermission::None, 2220 KMemoryPermission::None, KMemoryPermission::None,
2094 KMemoryAttribute::None, KMemoryAttribute::None) 2221 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2095 .IsSuccess());
2096 2222
2097 // Create an update allocator. 2223 // Create an update allocator.
2098 Result allocator_result{ResultSuccess}; 2224 Result allocator_result;
2099 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2225 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2100 m_memory_block_slab_manager); 2226 m_memory_block_slab_manager);
2227 R_TRY(allocator_result);
2228
2229 // We're going to perform an update, so create a helper.
2230 KScopedPageTableUpdater updater(this);
2101 2231
2102 // Perform mapping operation. 2232 // Perform mapping operation.
2103 if (is_pa_valid) { 2233 if (is_pa_valid) {
2104 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); 2234 const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
2235 R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));
2105 } else { 2236 } else {
2106 UNIMPLEMENTED(); 2237 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));
2107 } 2238 }
2108 2239
2109 // Update the blocks. 2240 // Update the blocks.
@@ -2116,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,
2116 R_SUCCEED(); 2247 R_SUCCEED();
2117} 2248}
2118 2249
2119Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { 2250Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
2120 ASSERT(this->IsLockedByCurrentThread()); 2251 KMemoryPermission perm) {
2252 // Check that the map is in range.
2253 const size_t size = num_pages * PageSize;
2254 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2121 2255
2122 VAddr cur_addr{addr}; 2256 // Lock the table.
2257 KScopedLightLock lk(m_general_lock);
2123 2258
2124 for (const auto& node : page_linked_list) { 2259 // Check the memory state.
2125 if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, 2260 size_t num_allocator_blocks;
2126 OperationType::Unmap)}; 2261 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2127 result.IsError()) { 2262 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2128 R_RETURN(result); 2263 KMemoryPermission::None, KMemoryAttribute::None,
2129 } 2264 KMemoryAttribute::None));
2130 2265
2131 cur_addr += node.GetNumPages() * PageSize; 2266 // Create an update allocator.
2132 } 2267 Result allocator_result;
2268 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2269 m_memory_block_slab_manager, num_allocator_blocks);
2270 R_TRY(allocator_result);
2271
2272 // We're going to perform an update, so create a helper.
2273 KScopedPageTableUpdater updater(this);
2274
2275 // Map the pages.
2276 R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm));
2277
2278 // Update the blocks.
2279 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm,
2280 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2281 KMemoryBlockDisableMergeAttribute::None);
2133 2282
2134 R_SUCCEED(); 2283 R_SUCCEED();
2135} 2284}
2136 2285
2137Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { 2286Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {
2138 // Check that the unmap is in range. 2287 // Check that the unmap is in range.
2139 const size_t num_pages{page_linked_list.GetNumPages()}; 2288 const size_t size = num_pages * PageSize;
2140 const size_t size{num_pages * PageSize};
2141 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2289 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
2142 2290
2143 // Lock the table. 2291 // Lock the table.
@@ -2151,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2151 KMemoryAttribute::None)); 2299 KMemoryAttribute::None));
2152 2300
2153 // Create an update allocator. 2301 // Create an update allocator.
2154 Result allocator_result{ResultSuccess}; 2302 Result allocator_result;
2155 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2303 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2156 m_memory_block_slab_manager, num_allocator_blocks); 2304 m_memory_block_slab_manager, num_allocator_blocks);
2157 R_TRY(allocator_result); 2305 R_TRY(allocator_result);
2158 2306
2307 // We're going to perform an update, so create a helper.
2308 KScopedPageTableUpdater updater(this);
2309
2159 // Perform the unmap. 2310 // Perform the unmap.
2160 R_TRY(UnmapPages(address, page_linked_list)); 2311 const KPageProperties unmap_properties = {KMemoryPermission::None, false, false,
2312 DisableMergeAttribute::None};
2313 R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));
2161 2314
2162 // Update the blocks. 2315 // Update the blocks.
2163 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2316 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2168,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo
2168 R_SUCCEED(); 2321 R_SUCCEED();
2169} 2322}
2170 2323
2171Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { 2324Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
2172 // Check that the unmap is in range. 2325 KProcessAddress region_start, size_t region_num_pages,
2326 KMemoryState state, KMemoryPermission perm) {
2327 ASSERT(!this->IsLockedByCurrentThread());
2328
2329 // Ensure this is a valid map request.
2330 const size_t num_pages = pg.GetNumPages();
2331 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
2332 ResultInvalidCurrentMemory);
2333 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
2334
2335 // Lock the table.
2336 KScopedLightLock lk(m_general_lock);
2337
2338 // Find a random address to map at.
2339 KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize,
2340 0, this->GetNumGuardPages());
2341 R_UNLESS(addr != 0, ResultOutOfMemory);
2342 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
2343 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
2344 KMemoryPermission::None, KMemoryPermission::None,
2345 KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);
2346
2347 // Create an update allocator.
2348 Result allocator_result;
2349 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2350 m_memory_block_slab_manager);
2351 R_TRY(allocator_result);
2352
2353 // We're going to perform an update, so create a helper.
2354 KScopedPageTableUpdater updater(this);
2355
2356 // Perform mapping operation.
2357 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2358 DisableMergeAttribute::DisableHead};
2359 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2360
2361 // Update the blocks.
2362 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2363 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2364 KMemoryBlockDisableMergeAttribute::None);
2365
2366 // We successfully mapped the pages.
2367 *out_addr = addr;
2368 R_SUCCEED();
2369}
2370
2371Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state,
2372 KMemoryPermission perm) {
2373 ASSERT(!this->IsLockedByCurrentThread());
2374
2375 // Ensure this is a valid map request.
2376 const size_t num_pages = pg.GetNumPages();
2173 const size_t size = num_pages * PageSize; 2377 const size_t size = num_pages * PageSize;
2174 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); 2378 R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);
2175 2379
2176 // Lock the table. 2380 // Lock the table.
2177 KScopedLightLock lk(m_general_lock); 2381 KScopedLightLock lk(m_general_lock);
2178 2382
2179 // Check the memory state. 2383 // Check if state allows us to map.
2180 size_t num_allocator_blocks{}; 2384 size_t num_allocator_blocks;
2385 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size,
2386 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None,
2387 KMemoryPermission::None, KMemoryAttribute::None,
2388 KMemoryAttribute::None));
2389
2390 // Create an update allocator.
2391 Result allocator_result;
2392 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2393 m_memory_block_slab_manager, num_allocator_blocks);
2394 R_TRY(allocator_result);
2395
2396 // We're going to perform an update, so create a helper.
2397 KScopedPageTableUpdater updater(this);
2398
2399 // Perform mapping operation.
2400 const KPageProperties properties = {perm, state == KMemoryState::Io, false,
2401 DisableMergeAttribute::DisableHead};
2402 R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
2403
2404 // Update the blocks.
2405 m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm,
2406 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2407 KMemoryBlockDisableMergeAttribute::None);
2408
2409 // We successfully mapped the pages.
2410 R_SUCCEED();
2411}
2412
2413Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg,
2414 KMemoryState state) {
2415 ASSERT(!this->IsLockedByCurrentThread());
2416
2417 // Ensure this is a valid unmap request.
2418 const size_t num_pages = pg.GetNumPages();
2419 const size_t size = num_pages * PageSize;
2420 R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory);
2421
2422 // Lock the table.
2423 KScopedLightLock lk(m_general_lock);
2424
2425 // Check if state allows us to unmap.
2426 size_t num_allocator_blocks;
2181 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, 2427 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
2182 KMemoryState::All, state, KMemoryPermission::None, 2428 KMemoryState::All, state, KMemoryPermission::None,
2183 KMemoryPermission::None, KMemoryAttribute::All, 2429 KMemoryPermission::None, KMemoryAttribute::All,
2184 KMemoryAttribute::None)); 2430 KMemoryAttribute::None));
2185 2431
2432 // Check that the page group is valid.
2433 R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory);
2434
2186 // Create an update allocator. 2435 // Create an update allocator.
2187 Result allocator_result{ResultSuccess}; 2436 Result allocator_result;
2188 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), 2437 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2189 m_memory_block_slab_manager, num_allocator_blocks); 2438 m_memory_block_slab_manager, num_allocator_blocks);
2190 R_TRY(allocator_result); 2439 R_TRY(allocator_result);
2191 2440
2192 // Perform the unmap. 2441 // We're going to perform an update, so create a helper.
2193 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); 2442 KScopedPageTableUpdater updater(this);
2443
2444 // Perform unmapping operation.
2445 const KPageProperties properties = {KMemoryPermission::None, false, false,
2446 DisableMergeAttribute::None};
2447 R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));
2194 2448
2195 // Update the blocks. 2449 // Update the blocks.
2196 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, 2450 m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free,
@@ -2550,54 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {
2550 } 2804 }
2551} 2805}
2552 2806
2553ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align,
2554 bool is_map_only, VAddr region_start,
2555 size_t region_num_pages, KMemoryState state,
2556 KMemoryPermission perm, PAddr map_addr) {
2557 KScopedLightLock lk(m_general_lock);
2558
2559 R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state),
2560 ResultInvalidCurrentMemory);
2561 R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory);
2562 const VAddr addr{
2563 AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)};
2564 R_UNLESS(addr, ResultOutOfMemory);
2565
2566 // Create an update allocator.
2567 Result allocator_result{ResultSuccess};
2568 KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),
2569 m_memory_block_slab_manager);
2570
2571 if (is_map_only) {
2572 R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
2573 } else {
2574 // Create a page group tohold the pages we allocate.
2575 KPageGroup pg{m_kernel, m_block_info_manager};
2576
2577 R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen(
2578 &pg, needed_num_pages,
2579 KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option)));
2580
2581 // Ensure that the page group is closed when we're done working with it.
2582 SCOPE_EXIT({ pg.Close(); });
2583
2584 // Clear all pages.
2585 for (const auto& it : pg) {
2586 std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()),
2587 m_heap_fill_value, it.GetSize());
2588 }
2589
2590 R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup));
2591 }
2592
2593 // Update the blocks.
2594 m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm,
2595 KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal,
2596 KMemoryBlockDisableMergeAttribute::None);
2597
2598 return addr;
2599}
2600
2601Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 2807Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
2602 KMemoryPermission perm, bool is_aligned, 2808 KMemoryPermission perm, bool is_aligned,
2603 bool check_heap) { 2809 bool check_heap) {
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 0a454b05b..367dab613 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -24,12 +24,36 @@ class System;
24 24
25namespace Kernel { 25namespace Kernel {
26 26
27enum class DisableMergeAttribute : u8 {
28 None = (0U << 0),
29 DisableHead = (1U << 0),
30 DisableHeadAndBody = (1U << 1),
31 EnableHeadAndBody = (1U << 2),
32 DisableTail = (1U << 3),
33 EnableTail = (1U << 4),
34 EnableAndMergeHeadBodyTail = (1U << 5),
35 EnableHeadBodyTail = EnableHeadAndBody | EnableTail,
36 DisableHeadBodyTail = DisableHeadAndBody | DisableTail,
37};
38
39struct KPageProperties {
40 KMemoryPermission perm;
41 bool io;
42 bool uncached;
43 DisableMergeAttribute disable_merge_attributes;
44};
45static_assert(std::is_trivial_v<KPageProperties>);
46static_assert(sizeof(KPageProperties) == sizeof(u32));
47
27class KBlockInfoManager; 48class KBlockInfoManager;
28class KMemoryBlockManager; 49class KMemoryBlockManager;
29class KResourceLimit; 50class KResourceLimit;
30class KSystemResource; 51class KSystemResource;
31 52
32class KPageTable final { 53class KPageTable final {
54protected:
55 struct PageLinkedList;
56
33public: 57public:
34 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; 58 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
35 59
@@ -57,27 +81,12 @@ public:
57 Result UnmapPhysicalMemory(VAddr addr, size_t size); 81 Result UnmapPhysicalMemory(VAddr addr, size_t size);
58 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 82 Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
59 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); 83 Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size);
60 Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
61 KMemoryPermission perm);
62 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr,
63 KMemoryState state, KMemoryPermission perm) {
64 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
65 this->GetRegionAddress(state),
66 this->GetRegionSize(state) / PageSize, state, perm));
67 }
68 Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
69 Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);
70 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm); 84 Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);
71 KMemoryInfo QueryInfo(VAddr addr); 85 KMemoryInfo QueryInfo(VAddr addr);
72 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm); 86 Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);
73 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr); 87 Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);
74 Result SetMaxHeapSize(size_t size); 88 Result SetMaxHeapSize(size_t size);
75 Result SetHeapSize(VAddr* out, size_t size); 89 Result SetHeapSize(VAddr* out, size_t size);
76 ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only,
77 VAddr region_start, size_t region_num_pages,
78 KMemoryState state, KMemoryPermission perm,
79 PAddr map_addr = 0);
80
81 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, 90 Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,
82 KMemoryPermission perm, bool is_aligned, bool check_heap); 91 KMemoryPermission perm, bool is_aligned, bool check_heap);
83 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); 92 Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap);
@@ -113,6 +122,40 @@ public:
113 122
114 bool CanContain(VAddr addr, size_t size, KMemoryState state) const; 123 bool CanContain(VAddr addr, size_t size, KMemoryState state) const;
115 124
125 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
126 KPhysicalAddress phys_addr, KProcessAddress region_start,
127 size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
128 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start,
129 region_num_pages, state, perm));
130 }
131
132 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
133 KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) {
134 R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
135 this->GetRegionAddress(state),
136 this->GetRegionSize(state) / PageSize, state, perm));
137 }
138
139 Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state,
140 KMemoryPermission perm) {
141 R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false,
142 this->GetRegionAddress(state),
143 this->GetRegionSize(state) / PageSize, state, perm));
144 }
145
146 Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state,
147 KMemoryPermission perm);
148 Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state);
149
150 Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
151 KProcessAddress region_start, size_t region_num_pages, KMemoryState state,
152 KMemoryPermission perm);
153 Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state,
154 KMemoryPermission perm);
155 Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state);
156 void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
157 const KPageGroup& pg);
158
116protected: 159protected:
117 struct PageLinkedList { 160 struct PageLinkedList {
118 private: 161 private:
@@ -166,11 +209,9 @@ private:
166 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr = 209 static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
167 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; 210 KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared;
168 211
169 Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); 212 Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
170 Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, 213 KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start,
171 bool is_pa_valid, VAddr region_start, size_t region_num_pages, 214 size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
172 KMemoryState state, KMemoryPermission perm);
173 Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
174 bool IsRegionContiguous(VAddr addr, u64 size) const; 215 bool IsRegionContiguous(VAddr addr, u64 size) const;
175 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list); 216 void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);
176 KMemoryInfo QueryInfoImpl(VAddr addr); 217 KMemoryInfo QueryInfoImpl(VAddr addr);
@@ -265,6 +306,11 @@ private:
265 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address, 306 void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,
266 size_t size, KMemoryPermission prot_perm); 307 size_t size, KMemoryPermission prot_perm);
267 308
309 Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address,
310 size_t num_pages, KMemoryPermission perm);
311 Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address,
312 const KPageGroup& pg, const KPageProperties properties, bool reuse_ll);
313
268 mutable KLightLock m_general_lock; 314 mutable KLightLock m_general_lock;
269 mutable KLightLock m_map_physical_memory_lock; 315 mutable KLightLock m_map_physical_memory_lock;
270 316
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index a1abf5d68..e201bb0cd 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
417} 417}
418 418
419void KProcess::Run(s32 main_thread_priority, u64 stack_size) { 419void KProcess::Run(s32 main_thread_priority, u64 stack_size) {
420 AllocateMainThreadStack(stack_size); 420 ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);
421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); 421 resource_limit->Reserve(LimitableResource::ThreadCountMax, 1);
422 resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);
423 422
424 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; 423 const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};
425 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); 424 ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError());
@@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {
675} 674}
676 675
677Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { 676Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
678 ASSERT(stack_size); 677 // Ensure that we haven't already allocated stack.
679 678 ASSERT(main_thread_stack_size == 0);
680 // The kernel always ensures that the given stack size is page aligned. 679
681 main_thread_stack_size = Common::AlignUp(stack_size, PageSize); 680 // Ensure that we're allocating a valid stack.
682 681 stack_size = Common::AlignUp(stack_size, PageSize);
683 const VAddr start{page_table.GetStackRegionStart()}; 682 // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory);
684 const std::size_t size{page_table.GetStackRegionEnd() - start}; 683 R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory);
685 684
686 CASCADE_RESULT(main_thread_stack_top, 685 // Place a tentative reservation of memory for our new stack.
687 page_table.AllocateAndMapMemory( 686 KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax,
688 main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, 687 stack_size);
689 KMemoryState::Stack, KMemoryPermission::UserReadWrite)); 688 R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached);
689
690 // Allocate and map our stack.
691 if (stack_size) {
692 KProcessAddress stack_bottom;
693 R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize,
694 KMemoryState::Stack, KMemoryPermission::UserReadWrite));
695
696 main_thread_stack_top = stack_bottom + stack_size;
697 main_thread_stack_size = stack_size;
698 }
690 699
691 main_thread_stack_top += main_thread_stack_size; 700 // We succeeded! Commit our memory reservation.
701 mem_reservation.Commit();
692 702
693 R_SUCCEED(); 703 R_SUCCEED();
694} 704}
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 3cf2b5d91..df505edfe 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -94,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m
94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); 94 R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);
95 } 95 }
96 96
97 return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, 97 return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared,
98 ConvertToKMemoryPermission(map_perm)); 98 ConvertToKMemoryPermission(map_perm));
99} 99}
100 100
101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) { 101Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
102 // Validate the size. 102 // Validate the size.
103 R_UNLESS(size == unmap_size, ResultInvalidSize); 103 R_UNLESS(size == unmap_size, ResultInvalidSize);
104 104
105 return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); 105 return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);
106} 106}
107 107
108} // namespace Kernel 108} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 21207fe99..84ff3c64b 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -330,7 +330,7 @@ void KThread::Finalize() {
330 KThread* const waiter = std::addressof(*it); 330 KThread* const waiter = std::addressof(*it);
331 331
332 // The thread shouldn't be a kernel waiter. 332 // The thread shouldn't be a kernel waiter.
333 ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); 333 ASSERT(!waiter->GetAddressKeyIsKernel());
334 334
335 // Clear the lock owner. 335 // Clear the lock owner.
336 waiter->SetLockOwner(nullptr); 336 waiter->SetLockOwner(nullptr);
@@ -763,19 +763,6 @@ void KThread::Continue() {
763 KScheduler::OnThreadStateChanged(kernel, this, old_state); 763 KScheduler::OnThreadStateChanged(kernel, this, old_state);
764} 764}
765 765
766void KThread::WaitUntilSuspended() {
767 // Make sure we have a suspend requested.
768 ASSERT(IsSuspendRequested());
769
770 // Loop until the thread is not executing on any core.
771 for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
772 KThread* core_thread{};
773 do {
774 core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
775 } while (core_thread == this);
776 }
777}
778
779Result KThread::SetActivity(Svc::ThreadActivity activity) { 766Result KThread::SetActivity(Svc::ThreadActivity activity) {
780 // Lock ourselves. 767 // Lock ourselves.
781 KScopedLightLock lk(activity_pause_lock); 768 KScopedLightLock lk(activity_pause_lock);
@@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
897 } 884 }
898 885
899 // Keep track of how many kernel waiters we have. 886 // Keep track of how many kernel waiters we have.
900 if (IsKernelAddressKey(thread->GetAddressKey())) { 887 if (thread->GetAddressKeyIsKernel()) {
901 ASSERT((num_kernel_waiters++) >= 0); 888 ASSERT((num_kernel_waiters++) >= 0);
902 KScheduler::SetSchedulerUpdateNeeded(kernel); 889 KScheduler::SetSchedulerUpdateNeeded(kernel);
903 } 890 }
@@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
911 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 898 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
912 899
913 // Keep track of how many kernel waiters we have. 900 // Keep track of how many kernel waiters we have.
914 if (IsKernelAddressKey(thread->GetAddressKey())) { 901 if (thread->GetAddressKeyIsKernel()) {
915 ASSERT((num_kernel_waiters--) > 0); 902 ASSERT((num_kernel_waiters--) > 0);
916 KScheduler::SetSchedulerUpdateNeeded(kernel); 903 KScheduler::SetSchedulerUpdateNeeded(kernel);
917 } 904 }
@@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
987 KThread* thread = std::addressof(*it); 974 KThread* thread = std::addressof(*it);
988 975
989 // Keep track of how many kernel waiters we have. 976 // Keep track of how many kernel waiters we have.
990 if (IsKernelAddressKey(thread->GetAddressKey())) { 977 if (thread->GetAddressKeyIsKernel()) {
991 ASSERT((num_kernel_waiters--) > 0); 978 ASSERT((num_kernel_waiters--) > 0);
992 KScheduler::SetSchedulerUpdateNeeded(kernel); 979 KScheduler::SetSchedulerUpdateNeeded(kernel);
993 } 980 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 7cd94a340..9d771de0e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -214,8 +214,6 @@ public:
214 214
215 void Continue(); 215 void Continue();
216 216
217 void WaitUntilSuspended();
218
219 constexpr void SetSyncedIndex(s32 index) { 217 constexpr void SetSyncedIndex(s32 index) {
220 synced_index = index; 218 synced_index = index;
221 } 219 }
@@ -607,13 +605,30 @@ public:
607 return address_key_value; 605 return address_key_value;
608 } 606 }
609 607
610 void SetAddressKey(VAddr key) { 608 [[nodiscard]] bool GetAddressKeyIsKernel() const {
609 return address_key_is_kernel;
610 }
611
612 //! NB: intentional deviation from official kernel.
613 //
614 // Separate SetAddressKey into user and kernel versions
615 // to cope with arbitrary host pointers making their way
616 // into things.
617
618 void SetUserAddressKey(VAddr key) {
611 address_key = key; 619 address_key = key;
620 address_key_is_kernel = false;
612 } 621 }
613 622
614 void SetAddressKey(VAddr key, u32 val) { 623 void SetUserAddressKey(VAddr key, u32 val) {
615 address_key = key; 624 address_key = key;
616 address_key_value = val; 625 address_key_value = val;
626 address_key_is_kernel = false;
627 }
628
629 void SetKernelAddressKey(VAddr key) {
630 address_key = key;
631 address_key_is_kernel = true;
617 } 632 }
618 633
619 void ClearWaitQueue() { 634 void ClearWaitQueue() {
@@ -772,6 +787,7 @@ private:
772 bool debug_attached{}; 787 bool debug_attached{};
773 s8 priority_inheritance_count{}; 788 s8 priority_inheritance_count{};
774 bool resource_limit_release_hint{}; 789 bool resource_limit_release_hint{};
790 bool address_key_is_kernel{};
775 StackParameters stack_parameters{}; 791 StackParameters stack_parameters{};
776 Common::SpinLock context_guard{}; 792 Common::SpinLock context_guard{};
777 793
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 1fb25f221..d9eafe261 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1198,28 +1198,35 @@ void KernelCore::Suspend(bool suspended) {
1198 const bool should_suspend{exception_exited || suspended}; 1198 const bool should_suspend{exception_exited || suspended};
1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; 1199 const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
1200 1200
1201 std::vector<KScopedAutoObject<KThread>> process_threads; 1201 //! This refers to the application process, not the current process.
1202 { 1202 KScopedAutoObject<KProcess> process = CurrentProcess();
1203 KScopedSchedulerLock sl{*this}; 1203 if (process.IsNull()) {
1204 return;
1205 }
1204 1206
1205 if (auto* process = CurrentProcess(); process != nullptr) { 1207 // Set the new activity.
1206 process->SetActivity(activity); 1208 process->SetActivity(activity);
1207 1209
1208 if (!should_suspend) { 1210 // Wait for process execution to stop.
1209 // Runnable now; no need to wait. 1211 bool must_wait{should_suspend};
1210 return; 1212
1211 } 1213 // KernelCore::Suspend must be called from locked context, or we
1214 // could race another call to SetActivity, interfering with waiting.
1215 while (must_wait) {
1216 KScopedSchedulerLock sl{*this};
1217
1218 // Assume that all threads have finished running.
1219 must_wait = false;
1212 1220
1213 for (auto* thread : process->GetThreadList()) { 1221 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1214 process_threads.emplace_back(thread); 1222 if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() ==
1223 process.GetPointerUnsafe()) {
1224 // A thread has not finished running yet.
1225 // Continue waiting.
1226 must_wait = true;
1215 } 1227 }
1216 } 1228 }
1217 } 1229 }
1218
1219 // Wait for execution to stop.
1220 for (auto& thread : process_threads) {
1221 thread->WaitUntilSuspended();
1222 }
1223} 1230}
1224 1231
1225void KernelCore::ShutdownCores() { 1232void KernelCore::ShutdownCores() {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index aca442196..67fa5d71c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1492,8 +1492,8 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p
1492 KMemoryAttribute::All, KMemoryAttribute::None)); 1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493 1493
1494 // Map the group. 1494 // Map the group.
1495 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1495 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
1496 KMemoryPermission::UserReadWrite)); 1496 KMemoryPermission::UserReadWrite));
1497 1497
1498 return ResultSuccess; 1498 return ResultSuccess;
1499} 1499}
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 2f871de31..5713f1288 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -272,6 +272,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
272 } 272 }
273 break; 273 break;
274 case Core::HID::NpadStyleIndex::JoyconLeft: 274 case Core::HID::NpadStyleIndex::JoyconLeft:
275 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
276 shared_memory->fullkey_color.fullkey = body_colors.left;
275 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 277 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
276 shared_memory->joycon_color.left = body_colors.left; 278 shared_memory->joycon_color.left = body_colors.left;
277 shared_memory->battery_level_dual = battery_level.left.battery_level; 279 shared_memory->battery_level_dual = battery_level.left.battery_level;
@@ -285,6 +287,8 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
285 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1); 287 shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
286 break; 288 break;
287 case Core::HID::NpadStyleIndex::JoyconRight: 289 case Core::HID::NpadStyleIndex::JoyconRight:
290 shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
291 shared_memory->fullkey_color.fullkey = body_colors.right;
288 shared_memory->joycon_color.attribute = ColorAttribute::Ok; 292 shared_memory->joycon_color.attribute = ColorAttribute::Ok;
289 shared_memory->joycon_color.right = body_colors.right; 293 shared_memory->joycon_color.right = body_colors.right;
290 shared_memory->battery_level_right = battery_level.right.battery_level; 294 shared_memory->battery_level_right = battery_level.right.battery_level;
@@ -332,6 +336,20 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
332 336
333 controller.is_connected = true; 337 controller.is_connected = true;
334 controller.device->Connect(); 338 controller.device->Connect();
339 controller.device->SetLedPattern();
340 if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
341 if (controller.is_dual_left_connected) {
342 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex,
343 Common::Input::PollingMode::Active);
344 }
345 if (controller.is_dual_right_connected) {
346 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
347 Common::Input::PollingMode::Active);
348 }
349 } else {
350 controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
351 Common::Input::PollingMode::Active);
352 }
335 SignalStyleSetChangedEvent(npad_id); 353 SignalStyleSetChangedEvent(npad_id);
336 WriteEmptyEntry(controller.shared_memory); 354 WriteEmptyEntry(controller.shared_memory);
337} 355}
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
index e5e50845f..17252a84a 100644
--- a/src/core/hle/service/hid/hidbus.cpp
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -297,13 +297,13 @@ void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
297 297
298 const auto parameters{rp.PopRaw<Parameters>()}; 298 const auto parameters{rp.PopRaw<Parameters>()};
299 299
300 LOG_INFO(Service_HID, 300 LOG_DEBUG(Service_HID,
301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, " 301 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}", 302 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
303 parameters.enable, parameters.bus_handle.abstracted_pad_id, 303 parameters.enable, parameters.bus_handle.abstracted_pad_id,
304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index, 304 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval, 305 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
306 parameters.applet_resource_user_id); 306 parameters.applet_resource_user_id);
307 307
308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle); 308 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
309 309
@@ -326,11 +326,11 @@ void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
326 IPC::RequestParser rp{ctx}; 326 IPC::RequestParser rp{ctx};
327 const auto bus_handle_{rp.PopRaw<BusHandle>()}; 327 const auto bus_handle_{rp.PopRaw<BusHandle>()};
328 328
329 LOG_INFO(Service_HID, 329 LOG_DEBUG(Service_HID,
330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, " 330 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
331 "is_valid={}", 331 "is_valid={}",
332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index, 332 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
333 bus_handle_.player_number, bus_handle_.is_valid); 333 bus_handle_.player_number, bus_handle_.is_valid);
334 334
335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_); 335 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
336 336
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
index 57f1a2a26..78ed47014 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.cpp
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/hid/emulated_devices.h" 4#include "core/hid/emulated_controller.h"
5#include "core/hid/hid_core.h" 5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h" 6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h" 7#include "core/hle/kernel/k_readable_event.h"
@@ -12,16 +12,20 @@ namespace Service::HID {
12RingController::RingController(Core::HID::HIDCore& hid_core_, 12RingController::RingController(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_) 13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) { 14 : HidbusBase(service_context_) {
15 input = hid_core_.GetEmulatedDevices(); 15 input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
16} 16}
17 17
18RingController::~RingController() = default; 18RingController::~RingController() = default;
19 19
20void RingController::OnInit() { 20void RingController::OnInit() {
21 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
22 Common::Input::PollingMode::Ring);
21 return; 23 return;
22} 24}
23 25
24void RingController::OnRelease() { 26void RingController::OnRelease() {
27 input->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
28 Common::Input::PollingMode::Active);
25 return; 29 return;
26}; 30};
27 31
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
index b37df50ac..845ce85a5 100644
--- a/src/core/hle/service/hid/hidbus/ringcon.h
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -9,7 +9,7 @@
9#include "core/hle/service/hid/hidbus/hidbus_base.h" 9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10 10
11namespace Core::HID { 11namespace Core::HID {
12class EmulatedDevices; 12class EmulatedController;
13} // namespace Core::HID 13} // namespace Core::HID
14 14
15namespace Service::HID { 15namespace Service::HID {
@@ -248,6 +248,6 @@ private:
248 .zero = {.value = idle_value, .crc = 225}, 248 .zero = {.value = idle_value, .crc = 225},
249 }; 249 };
250 250
251 Core::HID::EmulatedDevices* input; 251 Core::HID::EmulatedController* input;
252}; 252};
253} // namespace Service::HID 253} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 6a3453457..52f402c56 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -108,6 +108,8 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
108 auto result = IsIrCameraHandleValid(parameters.camera_handle); 108 auto result = IsIrCameraHandleValid(parameters.camera_handle);
109 if (result.IsSuccess()) { 109 if (result.IsSuccess()) {
110 // TODO: Stop Image processor 110 // TODO: Stop Image processor
111 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
112 Common::Input::PollingMode::Active);
111 result = ResultSuccess; 113 result = ResultSuccess;
112 } 114 }
113 115
@@ -139,6 +141,8 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
139 MakeProcessor<MomentProcessor>(parameters.camera_handle, device); 141 MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
140 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); 142 auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
141 image_transfer_processor.SetConfig(parameters.processor_config); 143 image_transfer_processor.SetConfig(parameters.processor_config);
144 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
145 Common::Input::PollingMode::IR);
142 } 146 }
143 147
144 IPC::ResponseBuilder rb{ctx, 2}; 148 IPC::ResponseBuilder rb{ctx, 2};
@@ -170,6 +174,8 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
170 auto& image_transfer_processor = 174 auto& image_transfer_processor =
171 GetProcessor<ClusteringProcessor>(parameters.camera_handle); 175 GetProcessor<ClusteringProcessor>(parameters.camera_handle);
172 image_transfer_processor.SetConfig(parameters.processor_config); 176 image_transfer_processor.SetConfig(parameters.processor_config);
177 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
178 Common::Input::PollingMode::IR);
173 } 179 }
174 180
175 IPC::ResponseBuilder rb{ctx, 2}; 181 IPC::ResponseBuilder rb{ctx, 2};
@@ -219,6 +225,8 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
219 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 225 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
220 image_transfer_processor.SetConfig(parameters.processor_config); 226 image_transfer_processor.SetConfig(parameters.processor_config);
221 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 227 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
228 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
229 Common::Input::PollingMode::IR);
222 } 230 }
223 231
224 IPC::ResponseBuilder rb{ctx, 2}; 232 IPC::ResponseBuilder rb{ctx, 2};
@@ -294,6 +302,8 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
294 auto& image_transfer_processor = 302 auto& image_transfer_processor =
295 GetProcessor<TeraPluginProcessor>(parameters.camera_handle); 303 GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
296 image_transfer_processor.SetConfig(parameters.processor_config); 304 image_transfer_processor.SetConfig(parameters.processor_config);
305 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
306 Common::Input::PollingMode::IR);
297 } 307 }
298 308
299 IPC::ResponseBuilder rb{ctx, 2}; 309 IPC::ResponseBuilder rb{ctx, 2};
@@ -343,6 +353,8 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
343 MakeProcessor<PointingProcessor>(camera_handle, device); 353 MakeProcessor<PointingProcessor>(camera_handle, device);
344 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); 354 auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
345 image_transfer_processor.SetConfig(processor_config); 355 image_transfer_processor.SetConfig(processor_config);
356 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
357 Common::Input::PollingMode::IR);
346 } 358 }
347 359
348 IPC::ResponseBuilder rb{ctx, 2}; 360 IPC::ResponseBuilder rb{ctx, 2};
@@ -453,6 +465,8 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
453 GetProcessor<ImageTransferProcessor>(parameters.camera_handle); 465 GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
454 image_transfer_processor.SetConfig(parameters.processor_config); 466 image_transfer_processor.SetConfig(parameters.processor_config);
455 image_transfer_processor.SetTransferMemoryPointer(transfer_memory); 467 image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
468 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
469 Common::Input::PollingMode::IR);
456 } 470 }
457 471
458 IPC::ResponseBuilder rb{ctx, 2}; 472 IPC::ResponseBuilder rb{ctx, 2};
@@ -479,6 +493,8 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
479 MakeProcessor<IrLedProcessor>(camera_handle, device); 493 MakeProcessor<IrLedProcessor>(camera_handle, device);
480 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); 494 auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
481 image_transfer_processor.SetConfig(processor_config); 495 image_transfer_processor.SetConfig(processor_config);
496 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
497 Common::Input::PollingMode::IR);
482 } 498 }
483 499
484 IPC::ResponseBuilder rb{ctx, 2}; 500 IPC::ResponseBuilder rb{ctx, 2};
@@ -504,6 +520,8 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
504 auto result = IsIrCameraHandleValid(parameters.camera_handle); 520 auto result = IsIrCameraHandleValid(parameters.camera_handle);
505 if (result.IsSuccess()) { 521 if (result.IsSuccess()) {
506 // TODO: Stop image processor async 522 // TODO: Stop image processor async
523 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
524 Common::Input::PollingMode::Active);
507 result = ResultSuccess; 525 result = ResultSuccess;
508 } 526 }
509 527
diff --git a/src/core/hle/service/nfc/nfc_device.cpp b/src/core/hle/service/nfc/nfc_device.cpp
index 78578f723..9a3234e8c 100644
--- a/src/core/hle/service/nfc/nfc_device.cpp
+++ b/src/core/hle/service/nfc/nfc_device.cpp
@@ -130,7 +130,9 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
130 return WrongDeviceState; 130 return WrongDeviceState;
131 } 131 }
132 132
133 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 133 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
134 Common::Input::PollingMode::NFC) !=
135 Common::Input::DriverResult::Success) {
134 LOG_ERROR(Service_NFC, "Nfc not supported"); 136 LOG_ERROR(Service_NFC, "Nfc not supported");
135 return NfcDisabled; 137 return NfcDisabled;
136 } 138 }
@@ -141,7 +143,8 @@ Result NfcDevice::StartDetection(NFP::TagProtocol allowed_protocol) {
141} 143}
142 144
143Result NfcDevice::StopDetection() { 145Result NfcDevice::StopDetection() {
144 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 146 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
147 Common::Input::PollingMode::Active);
145 148
146 if (device_state == NFP::DeviceState::Initialized) { 149 if (device_state == NFP::DeviceState::Initialized) {
147 return ResultSuccess; 150 return ResultSuccess;
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index c860fd1a1..e67a76f55 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -152,7 +152,9 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
152 return WrongDeviceState; 152 return WrongDeviceState;
153 } 153 }
154 154
155 if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) { 155 if (npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
156 Common::Input::PollingMode::NFC) !=
157 Common::Input::DriverResult::Success) {
156 LOG_ERROR(Service_NFP, "Nfc not supported"); 158 LOG_ERROR(Service_NFP, "Nfc not supported");
157 return NfcDisabled; 159 return NfcDisabled;
158 } 160 }
@@ -163,7 +165,8 @@ Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
163} 165}
164 166
165Result NfpDevice::StopDetection() { 167Result NfpDevice::StopDetection() {
166 npad_device->SetPollingMode(Common::Input::PollingMode::Active); 168 npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
169 Common::Input::PollingMode::Active);
167 170
168 if (device_state == DeviceState::Initialized) { 171 if (device_state == DeviceState::Initialized) {
169 return ResultSuccess; 172 return ResultSuccess;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 4e605fae4..af9660b55 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -440,7 +440,7 @@ struct Memory::Impl {
440 } 440 }
441 441
442 if (Settings::IsFastmemEnabled()) { 442 if (Settings::IsFastmemEnabled()) {
443 const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; 443 const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
444 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached); 444 system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
445 } 445 }
446 446
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index cef2c4d52..e3b627e4f 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -51,8 +51,29 @@ endif()
51 51
52if (ENABLE_SDL2) 52if (ENABLE_SDL2)
53 target_sources(input_common PRIVATE 53 target_sources(input_common PRIVATE
54 drivers/joycon.cpp
55 drivers/joycon.h
54 drivers/sdl_driver.cpp 56 drivers/sdl_driver.cpp
55 drivers/sdl_driver.h 57 drivers/sdl_driver.h
58 helpers/joycon_driver.cpp
59 helpers/joycon_driver.h
60 helpers/joycon_protocol/calibration.cpp
61 helpers/joycon_protocol/calibration.h
62 helpers/joycon_protocol/common_protocol.cpp
63 helpers/joycon_protocol/common_protocol.h
64 helpers/joycon_protocol/generic_functions.cpp
65 helpers/joycon_protocol/generic_functions.h
66 helpers/joycon_protocol/joycon_types.h
67 helpers/joycon_protocol/irs.cpp
68 helpers/joycon_protocol/irs.h
69 helpers/joycon_protocol/nfc.cpp
70 helpers/joycon_protocol/nfc.h
71 helpers/joycon_protocol/poller.cpp
72 helpers/joycon_protocol/poller.h
73 helpers/joycon_protocol/ringcon.cpp
74 helpers/joycon_protocol/ringcon.h
75 helpers/joycon_protocol/rumble.cpp
76 helpers/joycon_protocol/rumble.h
56 ) 77 )
57 target_link_libraries(input_common PRIVATE SDL2::SDL2) 78 target_link_libraries(input_common PRIVATE SDL2::SDL2)
58 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 79 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp
index fad9177dc..04970f635 100644
--- a/src/input_common/drivers/camera.cpp
+++ b/src/input_common/drivers/camera.cpp
@@ -72,11 +72,11 @@ std::size_t Camera::getImageHeight() const {
72 } 72 }
73} 73}
74 74
75Common::Input::CameraError Camera::SetCameraFormat( 75Common::Input::DriverResult Camera::SetCameraFormat(
76 [[maybe_unused]] const PadIdentifier& identifier_, 76 [[maybe_unused]] const PadIdentifier& identifier_,
77 const Common::Input::CameraFormat camera_format) { 77 const Common::Input::CameraFormat camera_format) {
78 status.format = camera_format; 78 status.format = camera_format;
79 return Common::Input::CameraError::None; 79 return Common::Input::DriverResult::Success;
80} 80}
81 81
82} // namespace InputCommon 82} // namespace InputCommon
diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h
index ead3e0fde..24b27e325 100644
--- a/src/input_common/drivers/camera.h
+++ b/src/input_common/drivers/camera.h
@@ -22,8 +22,8 @@ public:
22 std::size_t getImageWidth() const; 22 std::size_t getImageWidth() const;
23 std::size_t getImageHeight() const; 23 std::size_t getImageHeight() const;
24 24
25 Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_, 25 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_,
26 Common::Input::CameraFormat camera_format) override; 26 Common::Input::CameraFormat camera_format) override;
27 27
28private: 28private:
29 Common::Input::CameraStatus status{}; 29 Common::Input::CameraStatus status{};
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index 826fa2109..ecb3e9dc2 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -324,7 +324,7 @@ bool GCAdapter::GetGCEndpoint(libusb_device* device) {
324 return true; 324 return true;
325} 325}
326 326
327Common::Input::VibrationError GCAdapter::SetVibration( 327Common::Input::DriverResult GCAdapter::SetVibration(
328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 328 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f; 329 const auto mean_amplitude = (vibration.low_amplitude + vibration.high_amplitude) * 0.5f;
330 const auto processed_amplitude = 330 const auto processed_amplitude =
@@ -333,9 +333,9 @@ Common::Input::VibrationError GCAdapter::SetVibration(
333 pads[identifier.port].rumble_amplitude = processed_amplitude; 333 pads[identifier.port].rumble_amplitude = processed_amplitude;
334 334
335 if (!rumble_enabled) { 335 if (!rumble_enabled) {
336 return Common::Input::VibrationError::Disabled; 336 return Common::Input::DriverResult::Disabled;
337 } 337 }
338 return Common::Input::VibrationError::None; 338 return Common::Input::DriverResult::Success;
339} 339}
340 340
341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) { 341bool GCAdapter::IsVibrationEnabled([[maybe_unused]] const PadIdentifier& identifier) {
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index b5270fd0b..3c2eb376d 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -25,7 +25,7 @@ public:
25 explicit GCAdapter(std::string input_engine_); 25 explicit GCAdapter(std::string input_engine_);
26 ~GCAdapter() override; 26 ~GCAdapter() override;
27 27
28 Common::Input::VibrationError SetVibration( 28 Common::Input::DriverResult SetVibration(
29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 29 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
30 30
31 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 31 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
new file mode 100644
index 000000000..40cda400d
--- /dev/null
+++ b/src/input_common/drivers/joycon.cpp
@@ -0,0 +1,678 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <fmt/format.h>
5
6#include "common/param_package.h"
7#include "common/polyfill_ranges.h"
8#include "common/settings.h"
9#include "common/thread.h"
10#include "input_common/drivers/joycon.h"
11#include "input_common/helpers/joycon_driver.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon {
15
16Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
17 // Avoid conflicting with SDL driver
18 if (!Settings::values.enable_joycon_driver) {
19 return;
20 }
21 LOG_INFO(Input, "Joycon driver Initialization started");
22 const int init_res = SDL_hid_init();
23 if (init_res == 0) {
24 Setup();
25 } else {
26 LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
27 }
28}
29
30Joycons::~Joycons() {
31 Reset();
32}
33
34void Joycons::Reset() {
35 scan_thread = {};
36 for (const auto& device : left_joycons) {
37 if (!device) {
38 continue;
39 }
40 device->Stop();
41 }
42 for (const auto& device : right_joycons) {
43 if (!device) {
44 continue;
45 }
46 device->Stop();
47 }
48 SDL_hid_exit();
49}
50
51void Joycons::Setup() {
52 u32 port = 0;
53 PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
54 for (auto& device : left_joycons) {
55 PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
56 device = std::make_shared<Joycon::JoyconDriver>(port++);
57 }
58 port = 0;
59 for (auto& device : right_joycons) {
60 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
61 device = std::make_shared<Joycon::JoyconDriver>(port++);
62 }
63
64 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
65}
66
67void Joycons::ScanThread(std::stop_token stop_token) {
68 constexpr u16 nintendo_vendor_id = 0x057e;
69 Common::SetCurrentThreadName("JoyconScanThread");
70 while (!stop_token.stop_requested()) {
71 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
72 SDL_hid_device_info* cur_dev = devs;
73
74 while (cur_dev) {
75 if (IsDeviceNew(cur_dev)) {
76 LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
77 cur_dev->product_id);
78 RegisterNewDevice(cur_dev);
79 }
80 cur_dev = cur_dev->next;
81 }
82
83 SDL_hid_free_enumeration(devs);
84 std::this_thread::sleep_for(std::chrono::seconds(5));
85 }
86}
87
88bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
89 Joycon::ControllerType type{};
90 Joycon::SerialNumber serial_number{};
91
92 const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
93 if (result != Joycon::DriverResult::Success) {
94 return false;
95 }
96
97 const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
98 if (result2 != Joycon::DriverResult::Success) {
99 return false;
100 }
101
102 auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
103 if (!device) {
104 return false;
105 }
106 if (!device->IsConnected()) {
107 return false;
108 }
109 if (device->GetHandleSerialNumber() != serial_number) {
110 return false;
111 }
112 return true;
113 };
114
115 // Check if device already exist
116 switch (type) {
117 case Joycon::ControllerType::Left:
118 for (const auto& device : left_joycons) {
119 if (is_handle_identical(device)) {
120 return false;
121 }
122 }
123 break;
124 case Joycon::ControllerType::Right:
125 for (const auto& device : right_joycons) {
126 if (is_handle_identical(device)) {
127 return false;
128 }
129 }
130 break;
131 default:
132 return false;
133 }
134
135 return true;
136}
137
138void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
139 Joycon::ControllerType type{};
140 auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
141 auto handle = GetNextFreeHandle(type);
142 if (handle == nullptr) {
143 LOG_WARNING(Input, "No free handles available");
144 return;
145 }
146 if (result == Joycon::DriverResult::Success) {
147 result = handle->RequestDeviceAccess(device_info);
148 }
149 if (result == Joycon::DriverResult::Success) {
150 LOG_WARNING(Input, "Initialize device");
151
152 const std::size_t port = handle->GetDevicePort();
153 const Joycon::JoyconCallbacks callbacks{
154 .on_battery_data = {[this, port, type](Joycon::Battery value) {
155 OnBatteryUpdate(port, type, value);
156 }},
157 .on_color_data = {[this, port, type](Joycon::Color value) {
158 OnColorUpdate(port, type, value);
159 }},
160 .on_button_data = {[this, port, type](int id, bool value) {
161 OnButtonUpdate(port, type, id, value);
162 }},
163 .on_stick_data = {[this, port, type](int id, f32 value) {
164 OnStickUpdate(port, type, id, value);
165 }},
166 .on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
167 OnMotionUpdate(port, type, id, value);
168 }},
169 .on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
170 .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
171 OnAmiiboUpdate(port, amiibo_data);
172 }},
173 .on_camera_data = {[this, port](const std::vector<u8>& camera_data,
174 Joycon::IrsResolution format) {
175 OnCameraUpdate(port, camera_data, format);
176 }},
177 };
178
179 handle->InitializeDevice();
180 handle->SetCallbacks(callbacks);
181 }
182}
183
184std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
185 Joycon::ControllerType type) const {
186 if (type == Joycon::ControllerType::Left) {
187 const auto unconnected_device =
188 std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
189 if (unconnected_device != left_joycons.end()) {
190 return *unconnected_device;
191 }
192 }
193 if (type == Joycon::ControllerType::Right) {
194 const auto unconnected_device = std::ranges::find_if(
195 right_joycons, [](auto& device) { return !device->IsConnected(); });
196
197 if (unconnected_device != right_joycons.end()) {
198 return *unconnected_device;
199 }
200 }
201 return nullptr;
202}
203
204bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
205 const auto handle = GetHandle(identifier);
206 if (handle == nullptr) {
207 return false;
208 }
209 return handle->IsVibrationEnabled();
210}
211
212Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
213 const Common::Input::VibrationStatus& vibration) {
214 const Joycon::VibrationValue native_vibration{
215 .low_amplitude = vibration.low_amplitude,
216 .low_frequency = vibration.low_frequency,
217 .high_amplitude = vibration.high_amplitude,
218 .high_frequency = vibration.high_frequency,
219 };
220 auto handle = GetHandle(identifier);
221 if (handle == nullptr) {
222 return Common::Input::DriverResult::InvalidHandle;
223 }
224
225 handle->SetVibration(native_vibration);
226 return Common::Input::DriverResult::Success;
227}
228
229Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
230 const Common::Input::LedStatus& led_status) {
231 auto handle = GetHandle(identifier);
232 if (handle == nullptr) {
233 return Common::Input::DriverResult::InvalidHandle;
234 }
235 int led_config = led_status.led_1 ? 1 : 0;
236 led_config += led_status.led_2 ? 2 : 0;
237 led_config += led_status.led_3 ? 4 : 0;
238 led_config += led_status.led_4 ? 8 : 0;
239
240 return static_cast<Common::Input::DriverResult>(
241 handle->SetLedConfig(static_cast<u8>(led_config)));
242}
243
244Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
245 Common::Input::CameraFormat camera_format) {
246 auto handle = GetHandle(identifier);
247 if (handle == nullptr) {
248 return Common::Input::DriverResult::InvalidHandle;
249 }
250 return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
251 Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
252};
253
254Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
255 return Common::Input::NfcState::Success;
256};
257
258Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
259 const std::vector<u8>& data) {
260 return Common::Input::NfcState::NotSupported;
261};
262
263Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
264 const Common::Input::PollingMode polling_mode) {
265 auto handle = GetHandle(identifier);
266 if (handle == nullptr) {
267 LOG_ERROR(Input, "Invalid handle {}", identifier.port);
268 return Common::Input::DriverResult::InvalidHandle;
269 }
270
271 switch (polling_mode) {
272 case Common::Input::PollingMode::Active:
273 return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
274 case Common::Input::PollingMode::Pasive:
275 return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
276 case Common::Input::PollingMode::IR:
277 return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
278 case Common::Input::PollingMode::NFC:
279 return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
280 case Common::Input::PollingMode::Ring:
281 return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
282 default:
283 return Common::Input::DriverResult::NotSupported;
284 }
285}
286
287void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
288 Joycon::Battery value) {
289 const auto identifier = GetIdentifier(port, type);
290 if (value.charging != 0) {
291 SetBattery(identifier, Common::Input::BatteryLevel::Charging);
292 return;
293 }
294
295 Common::Input::BatteryLevel battery{};
296 switch (value.status) {
297 case 0:
298 battery = Common::Input::BatteryLevel::Empty;
299 break;
300 case 1:
301 battery = Common::Input::BatteryLevel::Critical;
302 break;
303 case 2:
304 battery = Common::Input::BatteryLevel::Low;
305 break;
306 case 3:
307 battery = Common::Input::BatteryLevel::Medium;
308 break;
309 case 4:
310 default:
311 battery = Common::Input::BatteryLevel::Full;
312 break;
313 }
314 SetBattery(identifier, battery);
315}
316
317void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
318 const Joycon::Color& value) {
319 const auto identifier = GetIdentifier(port, type);
320 Common::Input::BodyColorStatus color{
321 .body = value.body,
322 .buttons = value.buttons,
323 .left_grip = value.left_grip,
324 .right_grip = value.right_grip,
325 };
326 SetColor(identifier, color);
327}
328
329void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
330 const auto identifier = GetIdentifier(port, type);
331 SetButton(identifier, id, value);
332}
333
334void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
335 const auto identifier = GetIdentifier(port, type);
336 SetAxis(identifier, id, value);
337}
338
339void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
340 const Joycon::MotionData& value) {
341 const auto identifier = GetIdentifier(port, type);
342 BasicMotion motion_data{
343 .gyro_x = value.gyro_x,
344 .gyro_y = value.gyro_y,
345 .gyro_z = value.gyro_z,
346 .accel_x = value.accel_x,
347 .accel_y = value.accel_y,
348 .accel_z = value.accel_z,
349 .delta_timestamp = 15000,
350 };
351 SetMotion(identifier, id, motion_data);
352}
353
354void Joycons::OnRingConUpdate(f32 ring_data) {
355 // To simplify ring detection it will always be mapped to an empty identifier for all
356 // controllers
357 constexpr PadIdentifier identifier = {
358 .guid = Common::UUID{},
359 .port = 0,
360 .pad = 0,
361 };
362 SetAxis(identifier, 100, ring_data);
363}
364
365void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
366 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
367 const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
368 : Common::Input::NfcState::NewAmiibo;
369 SetNfc(identifier, {nfc_state, amiibo_data});
370}
371
372void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
373 Joycon::IrsResolution format) {
374 const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
375 SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
376}
377
378std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
379 auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
380 if (!device) {
381 return false;
382 }
383 if (!device->IsConnected()) {
384 return false;
385 }
386 if (device->GetDevicePort() == identifier.port) {
387 return true;
388 }
389 return false;
390 };
391 const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
392
393 if (type == Joycon::ControllerType::Left) {
394 const auto matching_device = std::ranges::find_if(
395 left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
396
397 if (matching_device != left_joycons.end()) {
398 return *matching_device;
399 }
400 }
401
402 if (type == Joycon::ControllerType::Right) {
403 const auto matching_device = std::ranges::find_if(
404 right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
405
406 if (matching_device != right_joycons.end()) {
407 return *matching_device;
408 }
409 }
410
411 return nullptr;
412}
413
414PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
415 const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
416 0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
417 return {
418 .guid = Common::UUID{guid},
419 .port = port,
420 .pad = static_cast<std::size_t>(type),
421 };
422}
423
424Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
425 const auto identifier = GetIdentifier(port, type);
426 return {
427 {"engine", GetEngineName()},
428 {"guid", identifier.guid.RawString()},
429 {"port", std::to_string(identifier.port)},
430 {"pad", std::to_string(identifier.pad)},
431 };
432}
433
434std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
435 std::vector<Common::ParamPackage> devices{};
436
437 auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
438 if (!device) {
439 return;
440 }
441 if (!device->IsConnected()) {
442 return;
443 }
444 auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
445 std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
446 device->GetDevicePort() + 1);
447 param.Set("display", std::move(name));
448 devices.emplace_back(param);
449 };
450
451 for (const auto& controller : left_joycons) {
452 add_entry(controller);
453 }
454 for (const auto& controller : right_joycons) {
455 add_entry(controller);
456 }
457
458 // List dual joycon pairs
459 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
460 if (!left_joycons[i] || !right_joycons[i]) {
461 continue;
462 }
463 if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
464 continue;
465 }
466 auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
467 const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
468 const auto type = Joycon::ControllerType::Dual;
469 std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
470
471 main_param.Set("display", std::move(name));
472 main_param.Set("guid2", second_param.Get("guid", ""));
473 main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
474 devices.emplace_back(main_param);
475 }
476
477 return devices;
478}
479
480ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
481 static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
482 18>
483 switch_to_joycon_button = {
484 std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
485 {Settings::NativeButton::B, Joycon::PadButton::B, true},
486 {Settings::NativeButton::X, Joycon::PadButton::X, true},
487 {Settings::NativeButton::Y, Joycon::PadButton::Y, true},
488 {Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
489 {Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
490 {Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
491 {Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
492 {Settings::NativeButton::L, Joycon::PadButton::L, false},
493 {Settings::NativeButton::R, Joycon::PadButton::R, true},
494 {Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
495 {Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
496 {Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
497 {Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
498 {Settings::NativeButton::Home, Joycon::PadButton::Home, true},
499 {Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
500 {Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
501 {Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
502 };
503
504 if (!params.Has("port")) {
505 return {};
506 }
507
508 ButtonMapping mapping{};
509 for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
510 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
511 auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
512 if (pad == Joycon::ControllerType::Dual) {
513 pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
514 }
515
516 Common::ParamPackage button_params = GetParamPackage(port, pad);
517 button_params.Set("button", static_cast<int>(joycon_button));
518 mapping.insert_or_assign(switch_button, std::move(button_params));
519 }
520
521 // Map SL and SR buttons for left joycons
522 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
523 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
524 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
525
526 Common::ParamPackage sl_button_params = button_params;
527 Common::ParamPackage sr_button_params = button_params;
528 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
529 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
530 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
531 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
532 }
533
534 // Map SL and SR buttons for right joycons
535 if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
536 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
537 Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
538
539 Common::ParamPackage sl_button_params = button_params;
540 Common::ParamPackage sr_button_params = button_params;
541 sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
542 sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
543 mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
544 mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
545 }
546
547 return mapping;
548}
549
550AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
551 if (!params.Has("port")) {
552 return {};
553 }
554
555 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
556 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
557 auto pad_right = pad_left;
558 if (pad_left == Joycon::ControllerType::Dual) {
559 pad_left = Joycon::ControllerType::Left;
560 pad_right = Joycon::ControllerType::Right;
561 }
562
563 AnalogMapping mapping = {};
564 Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
565 left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
566 left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
567 mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
568 Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
569 right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
570 right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
571 mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
572 return mapping;
573}
574
575MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
576 if (!params.Has("port")) {
577 return {};
578 }
579
580 const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
581 auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
582 auto pad_right = pad_left;
583 if (pad_left == Joycon::ControllerType::Dual) {
584 pad_left = Joycon::ControllerType::Left;
585 pad_right = Joycon::ControllerType::Right;
586 }
587
588 MotionMapping mapping = {};
589 Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
590 left_motion_params.Set("motion", 0);
591 mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
592 Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
593 right_Motion_params.Set("motion", 1);
594 mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
595 return mapping;
596}
597
598Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
599 const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
600 switch (button) {
601 case Joycon::PadButton::Left:
602 return Common::Input::ButtonNames::ButtonLeft;
603 case Joycon::PadButton::Right:
604 return Common::Input::ButtonNames::ButtonRight;
605 case Joycon::PadButton::Down:
606 return Common::Input::ButtonNames::ButtonDown;
607 case Joycon::PadButton::Up:
608 return Common::Input::ButtonNames::ButtonUp;
609 case Joycon::PadButton::LeftSL:
610 case Joycon::PadButton::RightSL:
611 return Common::Input::ButtonNames::TriggerSL;
612 case Joycon::PadButton::LeftSR:
613 case Joycon::PadButton::RightSR:
614 return Common::Input::ButtonNames::TriggerSR;
615 case Joycon::PadButton::L:
616 return Common::Input::ButtonNames::TriggerL;
617 case Joycon::PadButton::R:
618 return Common::Input::ButtonNames::TriggerR;
619 case Joycon::PadButton::ZL:
620 return Common::Input::ButtonNames::TriggerZL;
621 case Joycon::PadButton::ZR:
622 return Common::Input::ButtonNames::TriggerZR;
623 case Joycon::PadButton::A:
624 return Common::Input::ButtonNames::ButtonA;
625 case Joycon::PadButton::B:
626 return Common::Input::ButtonNames::ButtonB;
627 case Joycon::PadButton::X:
628 return Common::Input::ButtonNames::ButtonX;
629 case Joycon::PadButton::Y:
630 return Common::Input::ButtonNames::ButtonY;
631 case Joycon::PadButton::Plus:
632 return Common::Input::ButtonNames::ButtonPlus;
633 case Joycon::PadButton::Minus:
634 return Common::Input::ButtonNames::ButtonMinus;
635 case Joycon::PadButton::Home:
636 return Common::Input::ButtonNames::ButtonHome;
637 case Joycon::PadButton::Capture:
638 return Common::Input::ButtonNames::ButtonCapture;
639 case Joycon::PadButton::StickL:
640 return Common::Input::ButtonNames::ButtonStickL;
641 case Joycon::PadButton::StickR:
642 return Common::Input::ButtonNames::ButtonStickR;
643 default:
644 return Common::Input::ButtonNames::Undefined;
645 }
646}
647
648Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
649 if (params.Has("button")) {
650 return GetUIButtonName(params);
651 }
652 if (params.Has("axis")) {
653 return Common::Input::ButtonNames::Value;
654 }
655 if (params.Has("motion")) {
656 return Common::Input::ButtonNames::Engine;
657 }
658
659 return Common::Input::ButtonNames::Invalid;
660}
661
662std::string Joycons::JoyconName(Joycon::ControllerType type) const {
663 switch (type) {
664 case Joycon::ControllerType::Left:
665 return "Left Joycon";
666 case Joycon::ControllerType::Right:
667 return "Right Joycon";
668 case Joycon::ControllerType::Pro:
669 return "Pro Controller";
670 case Joycon::ControllerType::Grip:
671 return "Grip Controller";
672 case Joycon::ControllerType::Dual:
673 return "Dual Joycon";
674 default:
675 return "Unknown Joycon";
676 }
677}
678} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
new file mode 100644
index 000000000..316d383d8
--- /dev/null
+++ b/src/input_common/drivers/joycon.h
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include <thread>
9#include <SDL_hidapi.h>
10
11#include "input_common/input_engine.h"
12
13namespace InputCommon::Joycon {
14using SerialNumber = std::array<u8, 15>;
15struct Battery;
16struct Color;
17struct MotionData;
18enum class ControllerType;
19enum class DriverResult;
20enum class IrsResolution;
21class JoyconDriver;
22} // namespace InputCommon::Joycon
23
24namespace InputCommon {
25
26class Joycons final : public InputCommon::InputEngine {
27public:
28 explicit Joycons(const std::string& input_engine_);
29
30 ~Joycons();
31
32 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
33 Common::Input::DriverResult SetVibration(
34 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
35
36 Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,
37 const Common::Input::LedStatus& led_status) override;
38
39 Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,
40 Common::Input::CameraFormat camera_format) override;
41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
43 Common::Input::NfcState WriteNfcData(const PadIdentifier& identifier_,
44 const std::vector<u8>& data) override;
45
46 Common::Input::DriverResult SetPollingMode(
47 const PadIdentifier& identifier, const Common::Input::PollingMode polling_mode) override;
48
49 /// Used for automapping features
50 std::vector<Common::ParamPackage> GetInputDevices() const override;
51 ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
52 AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
53 MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
54 Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
55
56private:
57 static constexpr std::size_t MaxSupportedControllers = 8;
58
59 /// For shutting down, clear all data, join all threads, release usb devices
60 void Reset();
61
62 /// Registers controllers, clears all data and starts the scan thread
63 void Setup();
64
65 /// Actively searchs for new devices
66 void ScanThread(std::stop_token stop_token);
67
68 /// Returns true if device is valid and not registered
69 bool IsDeviceNew(SDL_hid_device_info* device_info) const;
70
71 /// Tries to connect to the new device
72 void RegisterNewDevice(SDL_hid_device_info* device_info);
73
74 /// Returns the next free handle
75 std::shared_ptr<Joycon::JoyconDriver> GetNextFreeHandle(Joycon::ControllerType type) const;
76
77 void OnBatteryUpdate(std::size_t port, Joycon::ControllerType type, Joycon::Battery value);
78 void OnColorUpdate(std::size_t port, Joycon::ControllerType type, const Joycon::Color& value);
79 void OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value);
80 void OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value);
81 void OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
82 const Joycon::MotionData& value);
83 void OnRingConUpdate(f32 ring_data);
84 void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data);
85 void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
86 Joycon::IrsResolution format);
87
88 /// Returns a JoyconHandle corresponding to a PadIdentifier
89 std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const;
90
91 /// Returns a PadIdentifier corresponding to the port number and joycon type
92 PadIdentifier GetIdentifier(std::size_t port, Joycon::ControllerType type) const;
93
94 /// Returns a ParamPackage corresponding to the port number and joycon type
95 Common::ParamPackage GetParamPackage(std::size_t port, Joycon::ControllerType type) const;
96
97 std::string JoyconName(std::size_t port) const;
98
99 Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
100
101 /// Returns the name of the device in text format
102 std::string JoyconName(Joycon::ControllerType type) const;
103
104 std::jthread scan_thread;
105
106 // Joycon types are split by type to ease supporting dualjoycon configurations
107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
109};
110
111} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index 9835d99d2..d975eb815 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -334,6 +334,15 @@ void SDLDriver::InitJoystick(int joystick_index) {
334 334
335 const auto guid = GetGUID(sdl_joystick); 335 const auto guid = GetGUID(sdl_joystick);
336 336
337 if (Settings::values.enable_joycon_driver) {
338 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e &&
339 (guid.uuid[8] == 0x06 || guid.uuid[8] == 0x07)) {
340 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
341 SDL_JoystickClose(sdl_joystick);
342 return;
343 }
344 }
345
337 std::scoped_lock lock{joystick_map_mutex}; 346 std::scoped_lock lock{joystick_map_mutex};
338 if (joystick_map.find(guid) == joystick_map.end()) { 347 if (joystick_map.find(guid) == joystick_map.end()) {
339 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 348 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -456,9 +465,13 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
456 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
457 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
458 467
459 // Use hidapi driver for joycons. This will allow joycons to be detected as a GameController and 468 // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled
460 // not a generic one 469 if (Settings::values.enable_joycon_driver) {
461 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 470 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
471 } else {
472 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
473 }
474 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
462 475
463 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 476 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
464 // driver on Linux. 477 // driver on Linux.
@@ -548,7 +561,7 @@ std::vector<Common::ParamPackage> SDLDriver::GetInputDevices() const {
548 return devices; 561 return devices;
549} 562}
550 563
551Common::Input::VibrationError SDLDriver::SetVibration( 564Common::Input::DriverResult SDLDriver::SetVibration(
552 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) { 565 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) {
553 const auto joystick = 566 const auto joystick =
554 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port)); 567 GetSDLJoystickByGUID(identifier.guid.RawString(), static_cast<int>(identifier.port));
@@ -582,7 +595,7 @@ Common::Input::VibrationError SDLDriver::SetVibration(
582 .vibration = new_vibration, 595 .vibration = new_vibration,
583 }); 596 });
584 597
585 return Common::Input::VibrationError::None; 598 return Common::Input::DriverResult::Success;
586} 599}
587 600
588bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) { 601bool SDLDriver::IsVibrationEnabled(const PadIdentifier& identifier) {
diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h
index 366bcc496..ffde169b3 100644
--- a/src/input_common/drivers/sdl_driver.h
+++ b/src/input_common/drivers/sdl_driver.h
@@ -63,7 +63,7 @@ public:
63 63
64 bool IsStickInverted(const Common::ParamPackage& params) override; 64 bool IsStickInverted(const Common::ParamPackage& params) override;
65 65
66 Common::Input::VibrationError SetVibration( 66 Common::Input::DriverResult SetVibration(
67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override; 67 const PadIdentifier& identifier, const Common::Input::VibrationStatus& vibration) override;
68 68
69 bool IsVibrationEnabled(const PadIdentifier& identifier) override; 69 bool IsVibrationEnabled(const PadIdentifier& identifier) override;
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 63ffaca67..4a0268a4d 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -22,22 +22,23 @@ VirtualAmiibo::VirtualAmiibo(std::string input_engine_) : InputEngine(std::move(
22 22
23VirtualAmiibo::~VirtualAmiibo() = default; 23VirtualAmiibo::~VirtualAmiibo() = default;
24 24
25Common::Input::PollingError VirtualAmiibo::SetPollingMode( 25Common::Input::DriverResult VirtualAmiibo::SetPollingMode(
26 [[maybe_unused]] const PadIdentifier& identifier_, 26 [[maybe_unused]] const PadIdentifier& identifier_,
27 const Common::Input::PollingMode polling_mode_) { 27 const Common::Input::PollingMode polling_mode_) {
28 polling_mode = polling_mode_; 28 polling_mode = polling_mode_;
29 29
30 if (polling_mode == Common::Input::PollingMode::NFC) { 30 switch (polling_mode) {
31 case Common::Input::PollingMode::NFC:
31 if (state == State::Initialized) { 32 if (state == State::Initialized) {
32 state = State::WaitingForAmiibo; 33 state = State::WaitingForAmiibo;
33 } 34 }
34 } else { 35 return Common::Input::DriverResult::Success;
36 default:
35 if (state == State::AmiiboIsOpen) { 37 if (state == State::AmiiboIsOpen) {
36 CloseAmiibo(); 38 CloseAmiibo();
37 } 39 }
40 return Common::Input::DriverResult::NotSupported;
38 } 41 }
39
40 return Common::Input::PollingError::None;
41} 42}
42 43
43Common::Input::NfcState VirtualAmiibo::SupportsNfc( 44Common::Input::NfcState VirtualAmiibo::SupportsNfc(
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 0f9dad333..13cacfc0a 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -36,7 +36,7 @@ public:
36 ~VirtualAmiibo() override; 36 ~VirtualAmiibo() override;
37 37
38 // Sets polling mode to a controller 38 // Sets polling mode to a controller
39 Common::Input::PollingError SetPollingMode( 39 Common::Input::DriverResult SetPollingMode(
40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override; 40 const PadIdentifier& identifier_, const Common::Input::PollingMode polling_mode_) override;
41 41
42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; 42 Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
new file mode 100644
index 000000000..3775e2d35
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -0,0 +1,575 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "common/swap.h"
6#include "common/thread.h"
7#include "input_common/helpers/joycon_driver.h"
8#include "input_common/helpers/joycon_protocol/calibration.h"
9#include "input_common/helpers/joycon_protocol/generic_functions.h"
10#include "input_common/helpers/joycon_protocol/irs.h"
11#include "input_common/helpers/joycon_protocol/nfc.h"
12#include "input_common/helpers/joycon_protocol/poller.h"
13#include "input_common/helpers/joycon_protocol/ringcon.h"
14#include "input_common/helpers/joycon_protocol/rumble.h"
15
16namespace InputCommon::Joycon {
17JoyconDriver::JoyconDriver(std::size_t port_) : port{port_} {
18 hidapi_handle = std::make_shared<JoyconHandle>();
19}
20
21JoyconDriver::~JoyconDriver() {
22 Stop();
23}
24
25void JoyconDriver::Stop() {
26 is_connected = false;
27 input_thread = {};
28}
29
30DriverResult JoyconDriver::RequestDeviceAccess(SDL_hid_device_info* device_info) {
31 std::scoped_lock lock{mutex};
32
33 handle_device_type = ControllerType::None;
34 GetDeviceType(device_info, handle_device_type);
35 if (handle_device_type == ControllerType::None) {
36 return DriverResult::UnsupportedControllerType;
37 }
38
39 hidapi_handle->handle =
40 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
41 std::memcpy(&handle_serial_number, device_info->serial_number, 15);
42 if (!hidapi_handle->handle) {
43 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
44 device_info->vendor_id, device_info->product_id);
45 return DriverResult::HandleInUse;
46 }
47 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
48 return DriverResult::Success;
49}
50
51DriverResult JoyconDriver::InitializeDevice() {
52 if (!hidapi_handle->handle) {
53 return DriverResult::InvalidHandle;
54 }
55 std::scoped_lock lock{mutex};
56 disable_input_thread = true;
57
58 // Reset Counters
59 error_counter = 0;
60 hidapi_handle->packet_counter = 0;
61
62 // Reset external device status
63 starlink_connected = false;
64 ring_connected = false;
65 amiibo_detected = false;
66
67 // Set HW default configuration
68 vibration_enabled = true;
69 motion_enabled = true;
70 hidbus_enabled = false;
71 nfc_enabled = false;
72 passive_enabled = false;
73 irs_enabled = false;
74 gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
75 gyro_performance = Joycon::GyroPerformance::HZ833;
76 accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
77 accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
78
79 // Initialize HW Protocols
80 calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);
81 generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle);
82 irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);
83 nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);
84 ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);
85 rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle);
86
87 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version);
89 generic_protocol->SetLowPowerMode(false);
90 generic_protocol->GetColor(color);
91 if (handle_device_type == ControllerType::Pro) {
92 // Some 3rd party controllers aren't pro controllers
93 generic_protocol->GetControllerType(device_type);
94 } else {
95 device_type = handle_device_type;
96 }
97 generic_protocol->GetSerialNumber(serial_number);
98 supported_features = GetSupportedFeatures();
99
100 // Get Calibration data
101 calibration_protocol->GetLeftJoyStickCalibration(left_stick_calibration);
102 calibration_protocol->GetRightJoyStickCalibration(right_stick_calibration);
103 calibration_protocol->GetImuCalibration(motion_calibration);
104
105 // Set led status
106 generic_protocol->SetLedBlinkPattern(static_cast<u8>(1 + port));
107
108 // Apply HW configuration
109 SetPollingMode();
110
111 // Initialize joycon poller
112 joycon_poller = std::make_unique<JoyconPoller>(device_type, left_stick_calibration,
113 right_stick_calibration, motion_calibration);
114
115 // Start pooling for data
116 is_connected = true;
117 if (!input_thread_running) {
118 input_thread =
119 std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
120 }
121
122 disable_input_thread = false;
123 return DriverResult::Success;
124}
125
126void JoyconDriver::InputThread(std::stop_token stop_token) {
127 LOG_INFO(Input, "Joycon Adapter input thread started");
128 Common::SetCurrentThreadName("JoyconInput");
129 input_thread_running = true;
130
131 // Max update rate is 5ms, ensure we are always able to read a bit faster
132 constexpr int ThreadDelay = 2;
133 std::vector<u8> buffer(MaxBufferSize);
134
135 while (!stop_token.stop_requested()) {
136 int status = 0;
137
138 if (!IsInputThreadValid()) {
139 input_thread.request_stop();
140 continue;
141 }
142
143 // By disabling the input thread we can ensure custom commands will succeed as no package is
144 // skipped
145 if (!disable_input_thread) {
146 status = SDL_hid_read_timeout(hidapi_handle->handle, buffer.data(), buffer.size(),
147 ThreadDelay);
148 } else {
149 std::this_thread::sleep_for(std::chrono::milliseconds(ThreadDelay));
150 }
151
152 if (IsPayloadCorrect(status, buffer)) {
153 OnNewData(buffer);
154 }
155
156 std::this_thread::yield();
157 }
158
159 is_connected = false;
160 input_thread_running = false;
161 LOG_INFO(Input, "Joycon Adapter input thread stopped");
162}
163
164void JoyconDriver::OnNewData(std::span<u8> buffer) {
165 const auto report_mode = static_cast<InputReport>(buffer[0]);
166
167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
168 // experience
169 switch (report_mode) {
170 case InputReport::STANDARD_FULL_60HZ:
171 case InputReport::NFC_IR_MODE_60HZ:
172 case InputReport::SIMPLE_HID_MODE: {
173 const auto now = std::chrono::steady_clock::now();
174 const auto new_delta_time = static_cast<u64>(
175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
176 delta_time = ((delta_time * 8) + (new_delta_time * 2)) / 10;
177 last_update = now;
178 joycon_poller->UpdateColor(color);
179 break;
180 }
181 default:
182 break;
183 }
184
185 const MotionStatus motion_status{
186 .is_enabled = motion_enabled,
187 .delta_time = delta_time,
188 .gyro_sensitivity = gyro_sensitivity,
189 .accelerometer_sensitivity = accelerometer_sensitivity,
190 };
191
192 // TODO: Remove this when calibration is properly loaded and not calculated
193 if (ring_connected && report_mode == InputReport::STANDARD_FULL_60HZ) {
194 InputReportActive data{};
195 memcpy(&data, buffer.data(), sizeof(InputReportActive));
196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
197 }
198
199 const RingStatus ring_status{
200 .is_enabled = ring_connected,
201 .default_value = ring_calibration.default_value,
202 .max_value = ring_calibration.max_value,
203 .min_value = ring_calibration.min_value,
204 };
205
206 if (irs_protocol->IsEnabled()) {
207 irs_protocol->RequestImage(buffer);
208 joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat());
209 }
210
211 if (nfc_protocol->IsEnabled()) {
212 if (amiibo_detected) {
213 if (!nfc_protocol->HasAmiibo()) {
214 joycon_poller->UpdateAmiibo({});
215 amiibo_detected = false;
216 return;
217 }
218 }
219
220 if (!amiibo_detected) {
221 std::vector<u8> data(0x21C);
222 const auto result = nfc_protocol->ScanAmiibo(data);
223 if (result == DriverResult::Success) {
224 joycon_poller->UpdateAmiibo(data);
225 amiibo_detected = true;
226 }
227 }
228 }
229
230 switch (report_mode) {
231 case InputReport::STANDARD_FULL_60HZ:
232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
233 break;
234 case InputReport::NFC_IR_MODE_60HZ:
235 joycon_poller->ReadNfcIRMode(buffer, motion_status);
236 break;
237 case InputReport::SIMPLE_HID_MODE:
238 joycon_poller->ReadPassiveMode(buffer);
239 break;
240 case InputReport::SUBCMD_REPLY:
241 LOG_DEBUG(Input, "Unhandled command reply");
242 break;
243 default:
244 LOG_ERROR(Input, "Report mode not Implemented {}", report_mode);
245 break;
246 }
247}
248
249DriverResult JoyconDriver::SetPollingMode() {
250 disable_input_thread = true;
251
252 rumble_protocol->EnableRumble(vibration_enabled && supported_features.vibration);
253
254 if (motion_enabled && supported_features.motion) {
255 generic_protocol->EnableImu(true);
256 generic_protocol->SetImuConfig(gyro_sensitivity, gyro_performance,
257 accelerometer_sensitivity, accelerometer_performance);
258 } else {
259 generic_protocol->EnableImu(false);
260 }
261
262 if (irs_protocol->IsEnabled()) {
263 irs_protocol->DisableIrs();
264 }
265
266 if (nfc_protocol->IsEnabled()) {
267 amiibo_detected = false;
268 nfc_protocol->DisableNfc();
269 }
270
271 if (ring_protocol->IsEnabled()) {
272 ring_connected = false;
273 ring_protocol->DisableRingCon();
274 }
275
276 if (irs_enabled && supported_features.irs) {
277 auto result = irs_protocol->EnableIrs();
278 if (result == DriverResult::Success) {
279 disable_input_thread = false;
280 return result;
281 }
282 irs_protocol->DisableIrs();
283 LOG_ERROR(Input, "Error enabling IRS");
284 }
285
286 if (nfc_enabled && supported_features.nfc) {
287 auto result = nfc_protocol->EnableNfc();
288 if (result == DriverResult::Success) {
289 result = nfc_protocol->StartNFCPollingMode();
290 }
291 if (result == DriverResult::Success) {
292 disable_input_thread = false;
293 return result;
294 }
295 nfc_protocol->DisableNfc();
296 LOG_ERROR(Input, "Error enabling NFC");
297 }
298
299 if (hidbus_enabled && supported_features.hidbus) {
300 auto result = ring_protocol->EnableRingCon();
301 if (result == DriverResult::Success) {
302 result = ring_protocol->StartRingconPolling();
303 }
304 if (result == DriverResult::Success) {
305 ring_connected = true;
306 disable_input_thread = false;
307 return result;
308 }
309 ring_connected = false;
310 ring_protocol->DisableRingCon();
311 LOG_ERROR(Input, "Error enabling Ringcon");
312 }
313
314 if (passive_enabled && supported_features.passive) {
315 const auto result = generic_protocol->EnablePassiveMode();
316 if (result == DriverResult::Success) {
317 disable_input_thread = false;
318 return result;
319 }
320 LOG_ERROR(Input, "Error enabling passive mode");
321 }
322
323 // Default Mode
324 const auto result = generic_protocol->EnableActiveMode();
325 if (result != DriverResult::Success) {
326 LOG_ERROR(Input, "Error enabling active mode");
327 }
328 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed();
330
331 disable_input_thread = false;
332 return result;
333}
334
335JoyconDriver::SupportedFeatures JoyconDriver::GetSupportedFeatures() {
336 SupportedFeatures features{
337 .passive = true,
338 .motion = true,
339 .vibration = true,
340 };
341
342 if (device_type == ControllerType::Right) {
343 features.nfc = true;
344 features.irs = true;
345 features.hidbus = true;
346 }
347
348 if (device_type == ControllerType::Pro) {
349 features.nfc = true;
350 }
351 return features;
352}
353
354bool JoyconDriver::IsInputThreadValid() const {
355 if (!is_connected.load()) {
356 return false;
357 }
358 if (hidapi_handle->handle == nullptr) {
359 return false;
360 }
361 // Controller is not responding. Terminate connection
362 if (error_counter > MaxErrorCount) {
363 return false;
364 }
365 return true;
366}
367
368bool JoyconDriver::IsPayloadCorrect(int status, std::span<const u8> buffer) {
369 if (status <= -1) {
370 error_counter++;
371 return false;
372 }
373 // There's no new data
374 if (status == 0) {
375 return false;
376 }
377 // No reply ever starts with zero
378 if (buffer[0] == 0x00) {
379 error_counter++;
380 return false;
381 }
382 error_counter = 0;
383 return true;
384}
385
386DriverResult JoyconDriver::SetVibration(const VibrationValue& vibration) {
387 std::scoped_lock lock{mutex};
388 if (disable_input_thread) {
389 return DriverResult::HandleInUse;
390 }
391 return rumble_protocol->SendVibration(vibration);
392}
393
394DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {
395 std::scoped_lock lock{mutex};
396 if (disable_input_thread) {
397 return DriverResult::HandleInUse;
398 }
399 return generic_protocol->SetLedPattern(led_pattern);
400}
401
402DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) {
403 std::scoped_lock lock{mutex};
404 if (disable_input_thread) {
405 return DriverResult::HandleInUse;
406 }
407 disable_input_thread = true;
408 const auto result = irs_protocol->SetIrsConfig(mode_, format_);
409 disable_input_thread = false;
410 return result;
411}
412
413DriverResult JoyconDriver::SetPasiveMode() {
414 std::scoped_lock lock{mutex};
415 motion_enabled = false;
416 hidbus_enabled = false;
417 nfc_enabled = false;
418 passive_enabled = true;
419 irs_enabled = false;
420 return SetPollingMode();
421}
422
423DriverResult JoyconDriver::SetActiveMode() {
424 if (is_ring_disabled_by_irs) {
425 is_ring_disabled_by_irs = false;
426 SetActiveMode();
427 return SetRingConMode();
428 }
429
430 std::scoped_lock lock{mutex};
431 motion_enabled = true;
432 hidbus_enabled = false;
433 nfc_enabled = false;
434 passive_enabled = false;
435 irs_enabled = false;
436 return SetPollingMode();
437}
438
439DriverResult JoyconDriver::SetIrMode() {
440 std::scoped_lock lock{mutex};
441
442 if (!supported_features.irs) {
443 return DriverResult::NotSupported;
444 }
445
446 if (ring_connected) {
447 is_ring_disabled_by_irs = true;
448 }
449
450 motion_enabled = false;
451 hidbus_enabled = false;
452 nfc_enabled = false;
453 passive_enabled = false;
454 irs_enabled = true;
455 return SetPollingMode();
456}
457
458DriverResult JoyconDriver::SetNfcMode() {
459 std::scoped_lock lock{mutex};
460
461 if (!supported_features.nfc) {
462 return DriverResult::NotSupported;
463 }
464
465 motion_enabled = true;
466 hidbus_enabled = false;
467 nfc_enabled = true;
468 passive_enabled = false;
469 irs_enabled = false;
470 return SetPollingMode();
471}
472
473DriverResult JoyconDriver::SetRingConMode() {
474 std::scoped_lock lock{mutex};
475
476 if (!supported_features.hidbus) {
477 return DriverResult::NotSupported;
478 }
479
480 motion_enabled = true;
481 hidbus_enabled = true;
482 nfc_enabled = false;
483 passive_enabled = false;
484 irs_enabled = false;
485
486 const auto result = SetPollingMode();
487
488 if (!ring_connected) {
489 return DriverResult::NoDeviceDetected;
490 }
491
492 return result;
493}
494
495bool JoyconDriver::IsConnected() const {
496 std::scoped_lock lock{mutex};
497 return is_connected.load();
498}
499
500bool JoyconDriver::IsVibrationEnabled() const {
501 std::scoped_lock lock{mutex};
502 return vibration_enabled;
503}
504
505FirmwareVersion JoyconDriver::GetDeviceVersion() const {
506 std::scoped_lock lock{mutex};
507 return version;
508}
509
510Color JoyconDriver::GetDeviceColor() const {
511 std::scoped_lock lock{mutex};
512 return color;
513}
514
515std::size_t JoyconDriver::GetDevicePort() const {
516 std::scoped_lock lock{mutex};
517 return port;
518}
519
520ControllerType JoyconDriver::GetDeviceType() const {
521 std::scoped_lock lock{mutex};
522 return device_type;
523}
524
525ControllerType JoyconDriver::GetHandleDeviceType() const {
526 std::scoped_lock lock{mutex};
527 return handle_device_type;
528}
529
530SerialNumber JoyconDriver::GetSerialNumber() const {
531 std::scoped_lock lock{mutex};
532 return serial_number;
533}
534
535SerialNumber JoyconDriver::GetHandleSerialNumber() const {
536 std::scoped_lock lock{mutex};
537 return handle_serial_number;
538}
539
540void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
541 joycon_poller->SetCallbacks(callbacks);
542}
543
544DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
545 ControllerType& controller_type) {
546 static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{
547 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
548 {0x2007, ControllerType::Right},
549 };
550 constexpr u16 nintendo_vendor_id = 0x057e;
551
552 controller_type = ControllerType::None;
553 if (device_info->vendor_id != nintendo_vendor_id) {
554 return DriverResult::UnsupportedControllerType;
555 }
556
557 for (const auto& [product_id, type] : supported_devices) {
558 if (device_info->product_id == static_cast<u16>(product_id)) {
559 controller_type = type;
560 return Joycon::DriverResult::Success;
561 }
562 }
563 return Joycon::DriverResult::UnsupportedControllerType;
564}
565
566DriverResult JoyconDriver::GetSerialNumber(SDL_hid_device_info* device_info,
567 SerialNumber& serial_number) {
568 if (device_info->serial_number == nullptr) {
569 return DriverResult::Unknown;
570 }
571 std::memcpy(&serial_number, device_info->serial_number, 15);
572 return Joycon::DriverResult::Success;
573}
574
575} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h
new file mode 100644
index 000000000..c1e189fa5
--- /dev/null
+++ b/src/input_common/helpers/joycon_driver.h
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <atomic>
7#include <functional>
8#include <mutex>
9#include <span>
10#include <thread>
11
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15class CalibrationProtocol;
16class GenericProtocol;
17class IrsProtocol;
18class NfcProtocol;
19class JoyconPoller;
20class RingConProtocol;
21class RumbleProtocol;
22
23class JoyconDriver final {
24public:
25 explicit JoyconDriver(std::size_t port_);
26
27 ~JoyconDriver();
28
29 DriverResult RequestDeviceAccess(SDL_hid_device_info* device_info);
30 DriverResult InitializeDevice();
31 void Stop();
32
33 bool IsConnected() const;
34 bool IsVibrationEnabled() const;
35
36 FirmwareVersion GetDeviceVersion() const;
37 Color GetDeviceColor() const;
38 std::size_t GetDevicePort() const;
39 ControllerType GetDeviceType() const;
40 ControllerType GetHandleDeviceType() const;
41 SerialNumber GetSerialNumber() const;
42 SerialNumber GetHandleSerialNumber() const;
43
44 DriverResult SetVibration(const VibrationValue& vibration);
45 DriverResult SetLedConfig(u8 led_pattern);
46 DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);
47 DriverResult SetPasiveMode();
48 DriverResult SetActiveMode();
49 DriverResult SetIrMode();
50 DriverResult SetNfcMode();
51 DriverResult SetRingConMode();
52
53 void SetCallbacks(const JoyconCallbacks& callbacks);
54
55 // Returns device type from hidapi handle
56 static DriverResult GetDeviceType(SDL_hid_device_info* device_info,
57 ControllerType& controller_type);
58
59 // Returns serial number from hidapi handle
60 static DriverResult GetSerialNumber(SDL_hid_device_info* device_info,
61 SerialNumber& serial_number);
62
63private:
64 struct SupportedFeatures {
65 bool passive{};
66 bool hidbus{};
67 bool irs{};
68 bool motion{};
69 bool nfc{};
70 bool vibration{};
71 };
72
73 /// Main thread, actively request new data from the handle
74 void InputThread(std::stop_token stop_token);
75
76 /// Called everytime a valid package arrives
77 void OnNewData(std::span<u8> buffer);
78
79 /// Updates device configuration to enable or disable features
80 DriverResult SetPollingMode();
81
82 /// Returns true if input thread is valid and doesn't need to be stopped
83 bool IsInputThreadValid() const;
84
85 /// Returns true if the data should be interpreted. Otherwise the error counter is incremented
86 bool IsPayloadCorrect(int status, std::span<const u8> buffer);
87
88 /// Returns a list of supported features that can be enabled on this device
89 SupportedFeatures GetSupportedFeatures();
90
91 // Protocol Features
92 std::unique_ptr<CalibrationProtocol> calibration_protocol;
93 std::unique_ptr<GenericProtocol> generic_protocol;
94 std::unique_ptr<IrsProtocol> irs_protocol;
95 std::unique_ptr<NfcProtocol> nfc_protocol;
96 std::unique_ptr<JoyconPoller> joycon_poller;
97 std::unique_ptr<RingConProtocol> ring_protocol;
98 std::unique_ptr<RumbleProtocol> rumble_protocol;
99
100 // Connection status
101 std::atomic<bool> is_connected{};
102 u64 delta_time;
103 std::size_t error_counter{};
104 std::shared_ptr<JoyconHandle> hidapi_handle;
105 std::chrono::time_point<std::chrono::steady_clock> last_update;
106
107 // External device status
108 bool starlink_connected{};
109 bool ring_connected{};
110 bool amiibo_detected{};
111 bool is_ring_disabled_by_irs{};
112
113 // Harware configuration
114 u8 leds{};
115 ReportMode mode{};
116 bool passive_enabled{}; // Low power mode, Ideal for multiple controllers at the same time
117 bool hidbus_enabled{}; // External device support
118 bool irs_enabled{}; // Infrared camera input
119 bool motion_enabled{}; // Enables motion input
120 bool nfc_enabled{}; // Enables Amiibo detection
121 bool vibration_enabled{}; // Allows vibrations
122
123 // Calibration data
124 GyroSensitivity gyro_sensitivity{};
125 GyroPerformance gyro_performance{};
126 AccelerometerSensitivity accelerometer_sensitivity{};
127 AccelerometerPerformance accelerometer_performance{};
128 JoyStickCalibration left_stick_calibration{};
129 JoyStickCalibration right_stick_calibration{};
130 MotionCalibration motion_calibration{};
131 RingCalibration ring_calibration{};
132
133 // Fixed joycon info
134 FirmwareVersion version{};
135 Color color{};
136 std::size_t port{};
137 ControllerType device_type{}; // Device type reported by controller
138 ControllerType handle_device_type{}; // Device type reported by hidapi
139 SerialNumber serial_number{}; // Serial number reported by controller
140 SerialNumber handle_serial_number{}; // Serial number type reported by hidapi
141 SupportedFeatures supported_features{};
142
143 // Thread related
144 mutable std::mutex mutex;
145 std::jthread input_thread;
146 bool input_thread_running{};
147 bool disable_input_thread{};
148};
149
150} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
new file mode 100644
index 000000000..f6e7e97d5
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -0,0 +1,184 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "input_common/helpers/joycon_protocol/calibration.h"
7#include "input_common/helpers/joycon_protocol/joycon_types.h"
8
9namespace InputCommon::Joycon {
10
11CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
12 : JoyconCommonProtocol(std::move(handle)) {}
13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this);
16 std::vector<u8> buffer;
17 DriverResult result{DriverResult::Success};
18 calibration = {};
19
20 result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer);
21
22 if (result == DriverResult::Success) {
23 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1;
24 if (has_user_calibration) {
25 result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer);
26 } else {
27 result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer);
28 }
29 }
30
31 if (result == DriverResult::Success) {
32 calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]);
33 calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4));
34 calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
35 calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
36 calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
37 calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
38 }
39
40 // Nintendo fix for drifting stick
41 // result = ReadSPI(0x60, 0x86 ,buffer, 16);
42 // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]);
43
44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration);
46
47 return result;
48}
49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this);
52 std::vector<u8> buffer;
53 DriverResult result{DriverResult::Success};
54 calibration = {};
55
56 result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer);
57
58 if (result == DriverResult::Success) {
59 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1;
60 if (has_user_calibration) {
61 result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer);
62 } else {
63 result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer);
64 }
65 }
66
67 if (result == DriverResult::Success) {
68 calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]);
69 calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4));
70 calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
71 calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
72 calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
73 calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
74 }
75
76 // Nintendo fix for drifting stick
77 // buffer = ReadSPI(0x60, 0x98 , 16);
78 // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]);
79
80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration);
82
83 return result;
84}
85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this);
88 std::vector<u8> buffer;
89 DriverResult result{DriverResult::Success};
90 calibration = {};
91
92 result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer);
93
94 if (result == DriverResult::Success) {
95 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1;
96 if (has_user_calibration) {
97 result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer);
98 } else {
99 result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer);
100 }
101 }
102
103 if (result == DriverResult::Success) {
104 IMUCalibration device_calibration{};
105 memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration));
106 calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0];
107 calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1];
108 calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2];
109
110 calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0];
111 calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1];
112 calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2];
113
114 calibration.gyro[0].offset = device_calibration.gyroscope_offset[0];
115 calibration.gyro[1].offset = device_calibration.gyroscope_offset[1];
116 calibration.gyro[2].offset = device_calibration.gyroscope_offset[2];
117
118 calibration.gyro[0].scale = device_calibration.gyroscope_scale[0];
119 calibration.gyro[1].scale = device_calibration.gyroscope_scale[1];
120 calibration.gyro[2].scale = device_calibration.gyroscope_scale[2];
121 }
122
123 ValidateCalibration(calibration);
124
125 return result;
126}
127
128DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
129 s16 current_value) {
130 // TODO: Get default calibration form ring itself
131 if (ring_data_max == 0 && ring_data_min == 0) {
132 ring_data_max = current_value + 800;
133 ring_data_min = current_value - 800;
134 ring_data_default = current_value;
135 }
136 ring_data_max = std::max(ring_data_max, current_value);
137 ring_data_min = std::min(ring_data_min, current_value);
138 calibration = {
139 .default_value = ring_data_default,
140 .max_value = ring_data_max,
141 .min_value = ring_data_min,
142 };
143 return DriverResult::Success;
144}
145
146void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
147 constexpr u16 DefaultStickCenter{2048};
148 constexpr u16 DefaultStickRange{1740};
149
150 if (calibration.x.center == 0xFFF || calibration.x.center == 0) {
151 calibration.x.center = DefaultStickCenter;
152 }
153 if (calibration.x.max == 0xFFF || calibration.x.max == 0) {
154 calibration.x.max = DefaultStickRange;
155 }
156 if (calibration.x.min == 0xFFF || calibration.x.min == 0) {
157 calibration.x.min = DefaultStickRange;
158 }
159
160 if (calibration.y.center == 0xFFF || calibration.y.center == 0) {
161 calibration.y.center = DefaultStickCenter;
162 }
163 if (calibration.y.max == 0xFFF || calibration.y.max == 0) {
164 calibration.y.max = DefaultStickRange;
165 }
166 if (calibration.y.min == 0xFFF || calibration.y.min == 0) {
167 calibration.y.min = DefaultStickRange;
168 }
169}
170
171void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
172 for (auto& sensor : calibration.accelerometer) {
173 if (sensor.scale == 0) {
174 sensor.scale = 0x4000;
175 }
176 }
177 for (auto& sensor : calibration.gyro) {
178 if (sensor.scale == 0) {
179 sensor.scale = 0x3be7;
180 }
181 }
182}
183
184} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
new file mode 100644
index 000000000..afb52a36a
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14
15namespace InputCommon::Joycon {
16enum class DriverResult;
17struct JoyStickCalibration;
18struct IMUCalibration;
19struct JoyconHandle;
20} // namespace InputCommon::Joycon
21
22namespace InputCommon::Joycon {
23
24/// Driver functions related to retrieving calibration data from the device
25class CalibrationProtocol final : private JoyconCommonProtocol {
26public:
27 explicit CalibrationProtocol(std::shared_ptr<JoyconHandle> handle);
28
29 /**
30 * Sends a request to obtain the left stick calibration from memory
31 * @param is_factory_calibration if true factory values will be returned
32 * @returns JoyStickCalibration of the left joystick
33 */
34 DriverResult GetLeftJoyStickCalibration(JoyStickCalibration& calibration);
35
36 /**
37 * Sends a request to obtain the right stick calibration from memory
38 * @param is_factory_calibration if true factory values will be returned
39 * @returns JoyStickCalibration of the right joystick
40 */
41 DriverResult GetRightJoyStickCalibration(JoyStickCalibration& calibration);
42
43 /**
44 * Sends a request to obtain the motion calibration from memory
45 * @returns ImuCalibration of the motion sensor
46 */
47 DriverResult GetImuCalibration(MotionCalibration& calibration);
48
49 /**
50 * Calculates on run time the proper calibration of the ring controller
51 * @returns RingCalibration of the ring sensor
52 */
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54
55private:
56 void ValidateCalibration(JoyStickCalibration& calibration);
57 void ValidateCalibration(MotionCalibration& calibration);
58
59 s16 ring_data_max = 0;
60 s16 ring_data_default = 0;
61 s16 ring_data_min = 0;
62};
63
64} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
new file mode 100644
index 000000000..417d0dcc5
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/common_protocol.h"
6
7namespace InputCommon::Joycon {
8JoyconCommonProtocol::JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_)
9 : hidapi_handle{std::move(hidapi_handle_)} {}
10
11u8 JoyconCommonProtocol::GetCounter() {
12 hidapi_handle->packet_counter = (hidapi_handle->packet_counter + 1) & 0x0F;
13 return hidapi_handle->packet_counter;
14}
15
16void JoyconCommonProtocol::SetBlocking() {
17 SDL_hid_set_nonblocking(hidapi_handle->handle, 0);
18}
19
20void JoyconCommonProtocol::SetNonBlocking() {
21 SDL_hid_set_nonblocking(hidapi_handle->handle, 1);
22}
23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 std::vector<u8> buffer;
26 const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer);
27 controller_type = ControllerType::None;
28
29 if (result == DriverResult::Success) {
30 controller_type = static_cast<ControllerType>(buffer[0]);
31 // Fallback to 3rd party pro controllers
32 if (controller_type == ControllerType::None) {
33 controller_type = ControllerType::Pro;
34 }
35 }
36
37 return result;
38}
39
40DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
41 ControllerType controller_type{ControllerType::None};
42 const auto result = GetDeviceType(controller_type);
43 if (result != DriverResult::Success || controller_type == ControllerType::None) {
44 return DriverResult::UnsupportedControllerType;
45 }
46
47 hidapi_handle->handle =
48 SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
49
50 if (!hidapi_handle->handle) {
51 LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
52 device_info->vendor_id, device_info->product_id);
53 return DriverResult::HandleInUse;
54 }
55
56 SetNonBlocking();
57 return DriverResult::Success;
58}
59
60DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
61 const std::array<u8, 1> buffer{static_cast<u8>(report_mode)};
62 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
63}
64
65DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) {
66 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
67
68 if (result == -1) {
69 return DriverResult::ErrorWritingData;
70 }
71
72 return DriverResult::Success;
73}
74
75DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector<u8>& output) {
76 constexpr int timeout_mili = 66;
77 constexpr int MaxTries = 15;
78 int tries = 0;
79 output.resize(MaxSubCommandResponseSize);
80
81 do {
82 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(),
83 MaxSubCommandResponseSize, timeout_mili);
84
85 if (result < 1) {
86 LOG_ERROR(Input, "No response from joycon");
87 }
88 if (tries++ > MaxTries) {
89 return DriverResult::Timeout;
90 }
91 } while (output[0] != 0x21 && output[14] != static_cast<u8>(sc));
92
93 if (output[0] != 0x21 && output[14] != static_cast<u8>(sc)) {
94 return DriverResult::WrongReply;
95 }
96
97 return DriverResult::Success;
98}
99
100DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
101 std::vector<u8>& output) {
102 std::vector<u8> local_buffer(MaxResponseSize);
103
104 local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD);
105 local_buffer[1] = GetCounter();
106 local_buffer[10] = static_cast<u8>(sc);
107 for (std::size_t i = 0; i < buffer.size(); ++i) {
108 local_buffer[11 + i] = buffer[i];
109 }
110
111 auto result = SendData(local_buffer);
112
113 if (result != DriverResult::Success) {
114 return result;
115 }
116
117 result = GetSubCommandResponse(sc, output);
118
119 return DriverResult::Success;
120}
121
122DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
123 std::vector<u8> output;
124 return SendSubCommand(sc, buffer, output);
125}
126
127DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
128 std::vector<u8> local_buffer(MaxResponseSize);
129
130 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA);
131 local_buffer[1] = GetCounter();
132 local_buffer[10] = static_cast<u8>(sc);
133 for (std::size_t i = 0; i < buffer.size(); ++i) {
134 local_buffer[11 + i] = buffer[i];
135 }
136
137 return SendData(local_buffer);
138}
139
140DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
141 std::vector<u8> local_buffer(MaxResponseSize);
142
143 local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY);
144 local_buffer[1] = GetCounter();
145
146 memcpy(local_buffer.data() + 2, buffer.data(), buffer.size());
147
148 return SendData(local_buffer);
149}
150
151DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) {
152 constexpr std::size_t MaxTries = 10;
153 std::size_t tries = 0;
154 std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size};
155 std::vector<u8> local_buffer(size + 20);
156
157 buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF);
158 buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8);
159 do {
160 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, local_buffer);
161 if (result != DriverResult::Success) {
162 return result;
163 }
164
165 if (tries++ > MaxTries) {
166 return DriverResult::Timeout;
167 }
168 } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]);
169
170 // Remove header from output
171 output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size);
172 return DriverResult::Success;
173}
174
175DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
176 const std::array<u8, 1> mcu_state{static_cast<u8>(enable ? 1 : 0)};
177 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
178
179 if (result != DriverResult::Success) {
180 LOG_ERROR(Input, "SendMCUData failed with error {}", result);
181 }
182
183 return result;
184}
185
186DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
187 LOG_DEBUG(Input, "ConfigureMCU");
188 std::array<u8, sizeof(MCUConfig)> config_buffer;
189 memcpy(config_buffer.data(), &config, sizeof(MCUConfig));
190 config_buffer[37] = CalculateMCU_CRC8(config_buffer.data() + 1, 36);
191
192 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
193
194 if (result != DriverResult::Success) {
195 LOG_ERROR(Input, "Set MCU config failed with error {}", result);
196 }
197
198 return result;
199}
200
201DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_,
202 std::vector<u8>& output) {
203 const int report_mode = static_cast<u8>(report_mode_);
204 constexpr int TimeoutMili = 200;
205 constexpr int MaxTries = 9;
206 int tries = 0;
207 output.resize(0x170);
208
209 do {
210 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 0x170, TimeoutMili);
211
212 if (result < 1) {
213 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
214 }
215 if (tries++ > MaxTries) {
216 return DriverResult::Timeout;
217 }
218 } while (output[0] != report_mode || output[49] == 0xFF);
219
220 if (output[0] != report_mode || output[49] == 0xFF) {
221 return DriverResult::WrongReply;
222 }
223
224 return DriverResult::Success;
225}
226
227DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
228 std::span<const u8> buffer,
229 std::vector<u8>& output) {
230 std::vector<u8> local_buffer(MaxResponseSize);
231
232 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA);
233 local_buffer[1] = GetCounter();
234 local_buffer[9] = static_cast<u8>(sc);
235 for (std::size_t i = 0; i < buffer.size(); ++i) {
236 local_buffer[10 + i] = buffer[i];
237 }
238
239 auto result = SendData(local_buffer);
240
241 if (result != DriverResult::Success) {
242 return result;
243 }
244
245 result = GetMCUDataResponse(report_mode, output);
246
247 return DriverResult::Success;
248}
249
250DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
251 std::vector<u8> output;
252 constexpr std::size_t MaxTries{8};
253 std::size_t tries{};
254
255 do {
256 const std::vector<u8> mcu_data{static_cast<u8>(MCUMode::Standby)};
257 const auto result = SendMCUData(report_mode, SubCommand::STATE, mcu_data, output);
258
259 if (result != DriverResult::Success) {
260 return result;
261 }
262
263 if (tries++ > MaxTries) {
264 return DriverResult::WrongReply;
265 }
266 } while (output[49] != 1 || output[56] != static_cast<u8>(mode));
267
268 return DriverResult::Success;
269}
270
271// crc-8-ccitt / polynomial 0x07 look up table
272constexpr std::array<u8, 256> mcu_crc8_table = {
273 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
274 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
275 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
276 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
277 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
278 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
279 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
280 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
281 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
282 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
283 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
284 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
285 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
286 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
287 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
288 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3};
289
290u8 JoyconCommonProtocol::CalculateMCU_CRC8(u8* buffer, u8 size) const {
291 u8 crc8 = 0x0;
292
293 for (int i = 0; i < size; ++i) {
294 crc8 = mcu_crc8_table[static_cast<u8>(crc8 ^ buffer[i])];
295 }
296 return crc8;
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
new file mode 100644
index 000000000..903bcf402
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -0,0 +1,173 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <memory>
12#include <span>
13#include <vector>
14
15#include "common/common_types.h"
16#include "input_common/helpers/joycon_protocol/joycon_types.h"
17
18namespace InputCommon::Joycon {
19
20/// Joycon driver functions that handle low level communication
21class JoyconCommonProtocol {
22public:
23 explicit JoyconCommonProtocol(std::shared_ptr<JoyconHandle> hidapi_handle_);
24
25 /**
26 * Sets handle to blocking. In blocking mode, SDL_hid_read() will wait (block) until there is
27 * data to read before returning.
28 */
29 void SetBlocking();
30
31 /**
32 * Sets handle to non blocking. In non-blocking mode calls to SDL_hid_read() will return
33 * immediately with a value of 0 if there is no data to be read
34 */
35 void SetNonBlocking();
36
37 /**
38 * Sends a request to obtain the joycon type from device
39 * @returns controller type of the joycon
40 */
41 DriverResult GetDeviceType(ControllerType& controller_type);
42
43 /**
44 * Verifies and sets the joycon_handle if device is valid
45 * @param device info from the driver
46 * @returns success if the device is valid
47 */
48 DriverResult CheckDeviceAccess(SDL_hid_device_info* device);
49
50 /**
51 * Sends a request to set the polling mode of the joycon
52 * @param report_mode polling mode to be set
53 */
54 DriverResult SetReportMode(Joycon::ReportMode report_mode);
55
56 /**
57 * Sends data to the joycon device
58 * @param buffer data to be send
59 */
60 DriverResult SendData(std::span<const u8> buffer);
61
62 /**
63 * Waits for incoming data of the joycon device that matchs the subcommand
64 * @param sub_command type of data to be returned
65 * @returns a buffer containing the responce
66 */
67 DriverResult GetSubCommandResponse(SubCommand sub_command, std::vector<u8>& output);
68
69 /**
70 * Sends a sub command to the device and waits for it's reply
71 * @param sc sub command to be send
72 * @param buffer data to be send
73 * @returns output buffer containing the responce
74 */
75 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output);
76
77 /**
78 * Sends a sub command to the device and waits for it's reply and ignores the output
79 * @param sc sub command to be send
80 * @param buffer data to be send
81 */
82 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer);
83
84 /**
85 * Sends a mcu command to the device
86 * @param sc sub command to be send
87 * @param buffer data to be send
88 */
89 DriverResult SendMCUCommand(SubCommand sc, std::span<const u8> buffer);
90
91 /**
92 * Sends vibration data to the joycon
93 * @param buffer data to be send
94 */
95 DriverResult SendVibrationReport(std::span<const u8> buffer);
96
97 /**
98 * Reads the SPI memory stored on the joycon
99 * @param Initial address location
100 * @param size in bytes to be read
101 * @returns output buffer containing the responce
102 */
103 DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output);
104
105 /**
106 * Enables MCU chip on the joycon
107 * @param enable if true the chip will be enabled
108 */
109 DriverResult EnableMCU(bool enable);
110
111 /**
112 * Configures the MCU to the correspoinding mode
113 * @param MCUConfig configuration
114 */
115 DriverResult ConfigureMCU(const MCUConfig& config);
116
117 /**
118 * Waits until there's MCU data available. On timeout returns error
119 * @param report mode of the expected reply
120 * @returns a buffer containing the responce
121 */
122 DriverResult GetMCUDataResponse(ReportMode report_mode_, std::vector<u8>& output);
123
124 /**
125 * Sends data to the MCU chip and waits for it's reply
126 * @param report mode of the expected reply
127 * @param sub command to be send
128 * @param buffer data to be send
129 * @returns output buffer containing the responce
130 */
131 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
132 std::vector<u8>& output);
133
134 /**
135 * Wait's until the MCU chip is on the specified mode
136 * @param report mode of the expected reply
137 * @param MCUMode configuration
138 */
139 DriverResult WaitSetMCUMode(ReportMode report_mode, MCUMode mode);
140
141 /**
142 * Calculates the checksum from the MCU data
143 * @param buffer containing the data to be send
144 * @param size of the buffer in bytes
145 * @returns byte with the correct checksum
146 */
147 u8 CalculateMCU_CRC8(u8* buffer, u8 size) const;
148
149private:
150 /**
151 * Increments and returns the packet counter of the handle
152 * @param joycon_handle device to send the data
153 * @returns packet counter value
154 */
155 u8 GetCounter();
156
157 std::shared_ptr<JoyconHandle> hidapi_handle;
158};
159
160class ScopedSetBlocking {
161public:
162 explicit ScopedSetBlocking(JoyconCommonProtocol* self) : m_self{self} {
163 m_self->SetBlocking();
164 }
165
166 ~ScopedSetBlocking() {
167 m_self->SetNonBlocking();
168 }
169
170private:
171 JoyconCommonProtocol* m_self{};
172};
173} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
new file mode 100644
index 000000000..63cfb1369
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -0,0 +1,136 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/generic_functions.h"
6
7namespace InputCommon::Joycon {
8
9GenericProtocol::GenericProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult GenericProtocol::EnablePassiveMode() {
13 ScopedSetBlocking sb(this);
14 return SetReportMode(ReportMode::SIMPLE_HID_MODE);
15}
16
17DriverResult GenericProtocol::EnableActiveMode() {
18 ScopedSetBlocking sb(this);
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20}
21
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26}
27
28DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31}
32
33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
34 ScopedSetBlocking sb(this);
35 std::vector<u8> output;
36
37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
38
39 device_info = {};
40 if (result == DriverResult::Success) {
41 memcpy(&device_info, output.data(), sizeof(DeviceInfo));
42 }
43
44 return result;
45}
46
47DriverResult GenericProtocol::GetControllerType(ControllerType& controller_type) {
48 return GetDeviceType(controller_type);
49}
50
51DriverResult GenericProtocol::EnableImu(bool enable) {
52 ScopedSetBlocking sb(this);
53 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
54 return SendSubCommand(SubCommand::ENABLE_IMU, buffer);
55}
56
57DriverResult GenericProtocol::SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
58 AccelerometerSensitivity asen,
59 AccelerometerPerformance afrec) {
60 ScopedSetBlocking sb(this);
61 const std::array<u8, 4> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
62 static_cast<u8>(gfrec), static_cast<u8>(afrec)};
63 return SendSubCommand(SubCommand::SET_IMU_SENSITIVITY, buffer);
64}
65
66DriverResult GenericProtocol::GetBattery(u32& battery_level) {
67 // This function is meant to request the high resolution battery status
68 battery_level = 0;
69 return DriverResult::NotSupported;
70}
71
72DriverResult GenericProtocol::GetColor(Color& color) {
73 ScopedSetBlocking sb(this);
74 std::vector<u8> buffer;
75 const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer);
76
77 color = {};
78 if (result == DriverResult::Success) {
79 color.body = static_cast<u32>((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]);
80 color.buttons = static_cast<u32>((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]);
81 color.left_grip = static_cast<u32>((buffer[6] << 16) | (buffer[7] << 8) | buffer[8]);
82 color.right_grip = static_cast<u32>((buffer[9] << 16) | (buffer[10] << 8) | buffer[11]);
83 }
84
85 return result;
86}
87
88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
89 ScopedSetBlocking sb(this);
90 std::vector<u8> buffer;
91 const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer);
92
93 serial_number = {};
94 if (result == DriverResult::Success) {
95 memcpy(serial_number.data(), buffer.data() + 1, sizeof(SerialNumber));
96 }
97
98 return result;
99}
100
101DriverResult GenericProtocol::GetTemperature(u32& temperature) {
102 // Not all devices have temperature sensor
103 temperature = 25;
104 return DriverResult::NotSupported;
105}
106
107DriverResult GenericProtocol::GetVersionNumber(FirmwareVersion& version) {
108 DeviceInfo device_info{};
109
110 const auto result = GetDeviceInfo(device_info);
111 version = device_info.firmware;
112
113 return result;
114}
115
116DriverResult GenericProtocol::SetHomeLight() {
117 ScopedSetBlocking sb(this);
118 static constexpr std::array<u8, 3> buffer{0x0f, 0xf0, 0x00};
119 return SendSubCommand(SubCommand::SET_HOME_LIGHT, buffer);
120}
121
122DriverResult GenericProtocol::SetLedBusy() {
123 return DriverResult::NotSupported;
124}
125
126DriverResult GenericProtocol::SetLedPattern(u8 leds) {
127 ScopedSetBlocking sb(this);
128 const std::array<u8, 1> buffer{leds};
129 return SendSubCommand(SubCommand::SET_PLAYER_LIGHTS, buffer);
130}
131
132DriverResult GenericProtocol::SetLedBlinkPattern(u8 leds) {
133 return SetLedPattern(static_cast<u8>(leds << 4));
134}
135
136} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
new file mode 100644
index 000000000..424831e81
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -0,0 +1,114 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include "input_common/helpers/joycon_protocol/common_protocol.h"
12#include "input_common/helpers/joycon_protocol/joycon_types.h"
13
14namespace InputCommon::Joycon {
15
16/// Joycon driver functions that easily implemented
17class GenericProtocol final : private JoyconCommonProtocol {
18public:
19 explicit GenericProtocol(std::shared_ptr<JoyconHandle> handle);
20
21 /// Enables passive mode. This mode only sends button data on change. Sticks will return digital
22 /// data instead of analog. Motion will be disabled
23 DriverResult EnablePassiveMode();
24
25 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode();
27
28 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable);
30
31 /// Unknown function used by the switch
32 DriverResult TriggersElapsed();
33
34 /**
35 * Sends a request to obtain the joycon firmware and mac from handle
36 * @returns controller device info
37 */
38 DriverResult GetDeviceInfo(DeviceInfo& controller_type);
39
40 /**
41 * Sends a request to obtain the joycon type from handle
42 * @returns controller type of the joycon
43 */
44 DriverResult GetControllerType(ControllerType& controller_type);
45
46 /**
47 * Enables motion input
48 * @param enable if true motion data will be enabled
49 */
50 DriverResult EnableImu(bool enable);
51
52 /**
53 * Configures the motion sensor with the specified parameters
54 * @param gsen gyroscope sensor sensitvity in degrees per second
55 * @param gfrec gyroscope sensor frequency in hertz
56 * @param asen accelerometer sensitivity in G force
57 * @param afrec accelerometer frequency in hertz
58 */
59 DriverResult SetImuConfig(GyroSensitivity gsen, GyroPerformance gfrec,
60 AccelerometerSensitivity asen, AccelerometerPerformance afrec);
61
62 /**
63 * Request battery level from the device
64 * @returns battery level
65 */
66 DriverResult GetBattery(u32& battery_level);
67
68 /**
69 * Request joycon colors from the device
70 * @returns colors of the body and buttons
71 */
72 DriverResult GetColor(Color& color);
73
74 /**
75 * Request joycon serial number from the device
76 * @returns 16 byte serial number
77 */
78 DriverResult GetSerialNumber(SerialNumber& serial_number);
79
80 /**
81 * Request joycon serial number from the device
82 * @returns 16 byte serial number
83 */
84 DriverResult GetTemperature(u32& temperature);
85
86 /**
87 * Request joycon serial number from the device
88 * @returns 16 byte serial number
89 */
90 DriverResult GetVersionNumber(FirmwareVersion& version);
91
92 /**
93 * Sets home led behaviour
94 */
95 DriverResult SetHomeLight();
96
97 /**
98 * Sets home led into a slow breathing state
99 */
100 DriverResult SetLedBusy();
101
102 /**
103 * Sets the 4 player leds on the joycon on a solid state
104 * @params bit flag containing the led state
105 */
106 DriverResult SetLedPattern(u8 leds);
107
108 /**
109 * Sets the 4 player leds on the joycon on a blinking state
110 * @returns bit flag containing the led state
111 */
112 DriverResult SetLedBlinkPattern(u8 leds);
113};
114} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
new file mode 100644
index 000000000..09e17bc5b
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -0,0 +1,298 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h"
7
8namespace InputCommon::Joycon {
9
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::IR,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37 if (result == DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 }
40 if (result == DriverResult::Success) {
41 result = ConfigureIrs();
42 }
43 if (result == DriverResult::Success) {
44 result = WriteRegistersStep1();
45 }
46 if (result == DriverResult::Success) {
47 result = WriteRegistersStep2();
48 }
49
50 is_enabled = true;
51
52 return result;
53}
54
55DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59
60 if (result == DriverResult::Success) {
61 result = EnableMCU(false);
62 }
63
64 is_enabled = false;
65
66 return result;
67}
68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode;
71 switch (format) {
72 case IrsResolution::Size320x240:
73 resolution_code = IrsResolutionCode::Size320x240;
74 fragments = IrsFragments::Size320x240;
75 resolution = IrsResolution::Size320x240;
76 break;
77 case IrsResolution::Size160x120:
78 resolution_code = IrsResolutionCode::Size160x120;
79 fragments = IrsFragments::Size160x120;
80 resolution = IrsResolution::Size160x120;
81 break;
82 case IrsResolution::Size80x60:
83 resolution_code = IrsResolutionCode::Size80x60;
84 fragments = IrsFragments::Size80x60;
85 resolution = IrsResolution::Size80x60;
86 break;
87 case IrsResolution::Size20x15:
88 resolution_code = IrsResolutionCode::Size20x15;
89 fragments = IrsFragments::Size20x15;
90 resolution = IrsResolution::Size20x15;
91 break;
92 case IrsResolution::Size40x30:
93 default:
94 resolution_code = IrsResolutionCode::Size40x30;
95 fragments = IrsFragments::Size40x30;
96 resolution = IrsResolution::Size40x30;
97 break;
98 }
99
100 // Restart feature
101 if (is_enabled) {
102 DisableIrs();
103 return EnableIrs();
104 }
105
106 return DriverResult::Success;
107}
108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112
113 if (buffer[0] == 0x31 && buffer[49] == 0x03) {
114 u8 new_packet_fragment = buffer[52];
115 if (new_packet_fragment == next_packet_fragment) {
116 packet_fragment = next_packet_fragment;
117 memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300);
118
119 return RequestFrame(packet_fragment);
120 }
121
122 if (new_packet_fragment == packet_fragment) {
123 return RequestFrame(packet_fragment);
124 }
125
126 return ResendFrame(next_packet_fragment);
127 }
128
129 return RequestFrame(packet_fragment);
130}
131
132DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28;
135 std::vector<u8> output;
136 std::size_t tries = 0;
137
138 const IrsConfigure irs_configuration{
139 .command = MCUCommand::ConfigureIR,
140 .sub_command = MCUSubCommand::SetDeviceMode,
141 .irs_mode = IrsMode::ImageTransfer,
142 .number_of_fragments = fragments,
143 .mcu_major_version = 0x0500,
144 .mcu_minor_version = 0x1800,
145 .crc = {},
146 };
147 buf_image.resize((static_cast<u8>(fragments) + 1) * 300);
148
149 std::array<u8, sizeof(IrsConfigure)> request_data{};
150 memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure));
151 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154
155 if (result != DriverResult::Success) {
156 return result;
157 }
158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply;
160 }
161 } while (output[15] != 0x0b);
162
163 return DriverResult::Success;
164}
165
166DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28;
170 std::vector<u8> output;
171 std::size_t tries = 0;
172
173 const IrsWriteRegisters irs_registers{
174 .command = MCUCommand::ConfigureIR,
175 .sub_command = MCUSubCommand::WriteDeviceRegisters,
176 .number_of_registers = 0x9,
177 .registers =
178 {
179 IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)},
180 {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)},
181 {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)},
182 {IrRegistersAddress::ExposureTime, 0x00},
183 {IrRegistersAddress::Leds, static_cast<u8>(leds)},
184 {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)},
185 {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)},
186 {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)},
187 {IrRegistersAddress::WhitePixelThreshold, 0xc8},
188 },
189 .crc = {},
190 };
191
192 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
193 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
194 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
195
196 std::array<u8, 38> mcu_request{0x02};
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF;
199
200 if (result != DriverResult::Success) {
201 return result;
202 }
203
204 do {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206
207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 }
211 if (result == DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 }
214
215 if (result != DriverResult::Success) {
216 return result;
217 }
218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply;
220 }
221 } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23);
222
223 return DriverResult::Success;
224}
225
226DriverResult IrsProtocol::WriteRegistersStep2() {
227 LOG_DEBUG(Input, "WriteRegistersStep2");
228 constexpr std::size_t max_tries = 28;
229 std::vector<u8> output;
230 std::size_t tries = 0;
231
232 const IrsWriteRegisters irs_registers{
233 .command = MCUCommand::ConfigureIR,
234 .sub_command = MCUSubCommand::WriteDeviceRegisters,
235 .number_of_registers = 0x8,
236 .registers =
237 {
238 IrsRegister{IrRegistersAddress::LedIntensitiyMSB,
239 static_cast<u8>(led_intensity >> 8)},
240 {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)},
241 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
242 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
243 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
244 {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)},
245 {IrRegistersAddress::UpdateTime, 0x2d},
246 {IrRegistersAddress::FinalizeConfig, 0x01},
247 },
248 .crc = {},
249 };
250
251 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
252 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
253 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
254 do {
255 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
256
257 if (result != DriverResult::Success) {
258 return result;
259 }
260 if (tries++ >= max_tries) {
261 return DriverResult::WrongReply;
262 }
263 } while (output[15] != 0x13 && output[15] != 0x23);
264
265 return DriverResult::Success;
266}
267
268DriverResult IrsProtocol::RequestFrame(u8 frame) {
269 std::array<u8, 38> mcu_request{};
270 mcu_request[3] = frame;
271 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
272 mcu_request[37] = 0xFF;
273 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
274}
275
276DriverResult IrsProtocol::ResendFrame(u8 frame) {
277 std::array<u8, 38> mcu_request{};
278 mcu_request[1] = 0x1;
279 mcu_request[2] = frame;
280 mcu_request[3] = 0x0;
281 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
282 mcu_request[37] = 0xFF;
283 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
284}
285
286std::vector<u8> IrsProtocol::GetImage() const {
287 return buf_image;
288}
289
290IrsResolution IrsProtocol::GetIrsFormat() const {
291 return resolution;
292}
293
294bool IrsProtocol::IsEnabled() const {
295 return is_enabled;
296}
297
298} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h
new file mode 100644
index 000000000..76dfa02ea
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.h
@@ -0,0 +1,63 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class IrsProtocol final : private JoyconCommonProtocol {
19public:
20 explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableIrs();
23
24 DriverResult DisableIrs();
25
26 DriverResult SetIrsConfig(IrsMode mode, IrsResolution format);
27
28 DriverResult RequestImage(std::span<u8> buffer);
29
30 std::vector<u8> GetImage() const;
31
32 IrsResolution GetIrsFormat() const;
33
34 bool IsEnabled() const;
35
36private:
37 DriverResult ConfigureIrs();
38
39 DriverResult WriteRegistersStep1();
40 DriverResult WriteRegistersStep2();
41
42 DriverResult RequestFrame(u8 frame);
43 DriverResult ResendFrame(u8 frame);
44
45 IrsMode irs_mode{IrsMode::ImageTransfer};
46 IrsResolution resolution{IrsResolution::Size40x30};
47 IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30};
48 IrsFragments fragments{IrsFragments::Size40x30};
49 IrLeds leds{IrLeds::BrightAndDim};
50 IrExLedFilter led_filter{IrExLedFilter::Enabled};
51 IrImageFlip image_flip{IrImageFlip::Normal};
52 u8 digital_gain{0x01};
53 u16 exposure{0x2490};
54 u16 led_intensity{0x0f10};
55 u32 denoise{0x012344};
56
57 u8 packet_fragment{};
58 std::vector<u8> buf_image; // 8bpp greyscale image.
59
60 bool is_enabled{};
61};
62
63} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
new file mode 100644
index 000000000..182d2c15b
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -0,0 +1,613 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <array>
12#include <functional>
13#include <SDL_hidapi.h>
14
15#include "common/bit_field.h"
16#include "common/common_funcs.h"
17#include "common/common_types.h"
18
19namespace InputCommon::Joycon {
20constexpr u32 MaxErrorCount = 50;
21constexpr u32 MaxBufferSize = 368;
22constexpr u32 MaxResponseSize = 49;
23constexpr u32 MaxSubCommandResponseSize = 64;
24constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
25
26using MacAddress = std::array<u8, 6>;
27using SerialNumber = std::array<u8, 15>;
28
29enum class ControllerType {
30 None,
31 Left,
32 Right,
33 Pro,
34 Grip,
35 Dual,
36};
37
38enum class PadAxes {
39 LeftStickX,
40 LeftStickY,
41 RightStickX,
42 RightStickY,
43 Undefined,
44};
45
46enum class PadMotion {
47 LeftMotion,
48 RightMotion,
49 Undefined,
50};
51
52enum class PadButton : u32 {
53 Down = 0x000001,
54 Up = 0x000002,
55 Right = 0x000004,
56 Left = 0x000008,
57 LeftSR = 0x000010,
58 LeftSL = 0x000020,
59 L = 0x000040,
60 ZL = 0x000080,
61 Y = 0x000100,
62 X = 0x000200,
63 B = 0x000400,
64 A = 0x000800,
65 RightSR = 0x001000,
66 RightSL = 0x002000,
67 R = 0x004000,
68 ZR = 0x008000,
69 Minus = 0x010000,
70 Plus = 0x020000,
71 StickR = 0x040000,
72 StickL = 0x080000,
73 Home = 0x100000,
74 Capture = 0x200000,
75};
76
77enum class PasivePadButton : u32 {
78 Down_A = 0x0001,
79 Right_X = 0x0002,
80 Left_B = 0x0004,
81 Up_Y = 0x0008,
82 SL = 0x0010,
83 SR = 0x0020,
84 Minus = 0x0100,
85 Plus = 0x0200,
86 StickL = 0x0400,
87 StickR = 0x0800,
88 Home = 0x1000,
89 Capture = 0x2000,
90 L_R = 0x4000,
91 ZL_ZR = 0x8000,
92};
93
94enum class OutputReport : u8 {
95 RUMBLE_AND_SUBCMD = 0x01,
96 FW_UPDATE_PKT = 0x03,
97 RUMBLE_ONLY = 0x10,
98 MCU_DATA = 0x11,
99 USB_CMD = 0x80,
100};
101
102enum class InputReport : u8 {
103 SUBCMD_REPLY = 0x21,
104 STANDARD_FULL_60HZ = 0x30,
105 NFC_IR_MODE_60HZ = 0x31,
106 SIMPLE_HID_MODE = 0x3F,
107 INPUT_USB_RESPONSE = 0x81,
108};
109
110enum class FeatureReport : u8 {
111 Last_SUBCMD = 0x02,
112 OTA_GW_UPGRADE = 0x70,
113 SETUP_MEM_READ = 0x71,
114 MEM_READ = 0x72,
115 ERASE_MEM_SECTOR = 0x73,
116 MEM_WRITE = 0x74,
117 LAUNCH = 0x75,
118};
119
120enum class SubCommand : u8 {
121 STATE = 0x00,
122 MANUAL_BT_PAIRING = 0x01,
123 REQ_DEV_INFO = 0x02,
124 SET_REPORT_MODE = 0x03,
125 TRIGGERS_ELAPSED = 0x04,
126 GET_PAGE_LIST_STATE = 0x05,
127 SET_HCI_STATE = 0x06,
128 RESET_PAIRING_INFO = 0x07,
129 LOW_POWER_MODE = 0x08,
130 SPI_FLASH_READ = 0x10,
131 SPI_FLASH_WRITE = 0x11,
132 SPI_SECTOR_ERASE = 0x12,
133 RESET_MCU = 0x20,
134 SET_MCU_CONFIG = 0x21,
135 SET_MCU_STATE = 0x22,
136 SET_PLAYER_LIGHTS = 0x30,
137 GET_PLAYER_LIGHTS = 0x31,
138 SET_HOME_LIGHT = 0x38,
139 ENABLE_IMU = 0x40,
140 SET_IMU_SENSITIVITY = 0x41,
141 WRITE_IMU_REG = 0x42,
142 READ_IMU_REG = 0x43,
143 ENABLE_VIBRATION = 0x48,
144 GET_REGULATED_VOLTAGE = 0x50,
145 SET_EXTERNAL_CONFIG = 0x58,
146 UNKNOWN_RINGCON = 0x59,
147 UNKNOWN_RINGCON2 = 0x5A,
148 UNKNOWN_RINGCON3 = 0x5C,
149};
150
151enum class UsbSubCommand : u8 {
152 CONN_STATUS = 0x01,
153 HADSHAKE = 0x02,
154 BAUDRATE_3M = 0x03,
155 NO_TIMEOUT = 0x04,
156 EN_TIMEOUT = 0x05,
157 RESET = 0x06,
158 PRE_HANDSHAKE = 0x91,
159 SEND_UART = 0x92,
160};
161
162enum class CalMagic : u8 {
163 USR_MAGIC_0 = 0xB2,
164 USR_MAGIC_1 = 0xA1,
165 USRR_MAGI_SIZE = 2,
166};
167
168enum class CalAddr {
169 SERIAL_NUMBER = 0X6000,
170 DEVICE_TYPE = 0X6012,
171 COLOR_EXIST = 0X601B,
172 FACT_LEFT_DATA = 0X603d,
173 FACT_RIGHT_DATA = 0X6046,
174 COLOR_DATA = 0X6050,
175 FACT_IMU_DATA = 0X6020,
176 USER_LEFT_MAGIC = 0X8010,
177 USER_LEFT_DATA = 0X8012,
178 USER_RIGHT_MAGIC = 0X801B,
179 USER_RIGHT_DATA = 0X801D,
180 USER_IMU_MAGIC = 0X8026,
181 USER_IMU_DATA = 0X8028,
182};
183
184enum class ReportMode : u8 {
185 ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
186 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
187 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
188 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
189 MCU_UPDATE_STATE = 0x23,
190 STANDARD_FULL_60HZ = 0x30,
191 NFC_IR_MODE_60HZ = 0x31,
192 SIMPLE_HID_MODE = 0x3F,
193};
194
195enum class GyroSensitivity : u8 {
196 DPS250,
197 DPS500,
198 DPS1000,
199 DPS2000, // Default
200};
201
202enum class AccelerometerSensitivity : u8 {
203 G8, // Default
204 G4,
205 G2,
206 G16,
207};
208
209enum class GyroPerformance : u8 {
210 HZ833,
211 HZ208, // Default
212};
213
214enum class AccelerometerPerformance : u8 {
215 HZ200,
216 HZ100, // Default
217};
218
219enum class MCUCommand : u8 {
220 ConfigureMCU = 0x21,
221 ConfigureIR = 0x23,
222};
223
224enum class MCUSubCommand : u8 {
225 SetMCUMode = 0x0,
226 SetDeviceMode = 0x1,
227 ReadDeviceMode = 0x02,
228 WriteDeviceRegisters = 0x4,
229};
230
231enum class MCUMode : u8 {
232 Suspend = 0,
233 Standby = 1,
234 Ringcon = 3,
235 NFC = 4,
236 IR = 5,
237 MaybeFWUpdate = 6,
238};
239
240enum class MCURequest : u8 {
241 GetMCUStatus = 1,
242 GetNFCData = 2,
243 GetIRData = 3,
244};
245
246enum class MCUReport : u8 {
247 Empty = 0x00,
248 StateReport = 0x01,
249 IRData = 0x03,
250 BusyInitializing = 0x0b,
251 IRStatus = 0x13,
252 IRRegisters = 0x1b,
253 NFCState = 0x2a,
254 NFCReadData = 0x3a,
255 EmptyAwaitingCmd = 0xff,
256};
257
258enum class MCUPacketFlag : u8 {
259 MorePacketsRemaining = 0x00,
260 LastCommandPacket = 0x08,
261};
262
263enum class NFCReadCommand : u8 {
264 CancelAll = 0x00,
265 StartPolling = 0x01,
266 StopPolling = 0x02,
267 StartWaitingRecieve = 0x04,
268 Ntag = 0x06,
269 Mifare = 0x0F,
270};
271
272enum class NFCTagType : u8 {
273 AllTags = 0x00,
274 Ntag215 = 0x01,
275};
276
277enum class NFCPages {
278 Block0 = 0,
279 Block45 = 45,
280 Block135 = 135,
281 Block231 = 231,
282};
283
284enum class NFCStatus : u8 {
285 LastPackage = 0x04,
286 TagLost = 0x07,
287};
288
289enum class IrsMode : u8 {
290 None = 0x02,
291 Moment = 0x03,
292 Dpd = 0x04,
293 Clustering = 0x06,
294 ImageTransfer = 0x07,
295 Silhouette = 0x08,
296 TeraImage = 0x09,
297 SilhouetteTeraImage = 0x0A,
298};
299
300enum class IrsResolution {
301 Size320x240,
302 Size160x120,
303 Size80x60,
304 Size40x30,
305 Size20x15,
306 None,
307};
308
309enum class IrsResolutionCode : u8 {
310 Size320x240 = 0x00, // Full pixel array
311 Size160x120 = 0x50, // Sensor Binning [2 X 2]
312 Size80x60 = 0x64, // Sensor Binning [4 x 2] and Skipping [1 x 2]
313 Size40x30 = 0x69, // Sensor Binning [4 x 2] and Skipping [2 x 4]
314 Size20x15 = 0x6A, // Sensor Binning [4 x 2] and Skipping [4 x 4]
315};
316
317// Size of image divided by 300
318enum class IrsFragments : u8 {
319 Size20x15 = 0x00,
320 Size40x30 = 0x03,
321 Size80x60 = 0x0f,
322 Size160x120 = 0x3f,
323 Size320x240 = 0xFF,
324};
325
326enum class IrLeds : u8 {
327 BrightAndDim = 0x00,
328 Bright = 0x20,
329 Dim = 0x10,
330 None = 0x30,
331};
332
333enum class IrExLedFilter : u8 {
334 Disabled = 0x00,
335 Enabled = 0x03,
336};
337
338enum class IrImageFlip : u8 {
339 Normal = 0x00,
340 Inverted = 0x02,
341};
342
343enum class IrRegistersAddress : u16 {
344 UpdateTime = 0x0400,
345 FinalizeConfig = 0x0700,
346 LedFilter = 0x0e00,
347 Leds = 0x1000,
348 LedIntensitiyMSB = 0x1100,
349 LedIntensitiyLSB = 0x1200,
350 ImageFlip = 0x2d00,
351 Resolution = 0x2e00,
352 DigitalGainLSB = 0x2e01,
353 DigitalGainMSB = 0x2f01,
354 ExposureLSB = 0x3001,
355 ExposureMSB = 0x3101,
356 ExposureTime = 0x3201,
357 WhitePixelThreshold = 0x4301,
358 DenoiseSmoothing = 0x6701,
359 DenoiseEdge = 0x6801,
360 DenoiseColor = 0x6901,
361};
362
363enum class DriverResult {
364 Success,
365 WrongReply,
366 Timeout,
367 UnsupportedControllerType,
368 HandleInUse,
369 ErrorReadingData,
370 ErrorWritingData,
371 NoDeviceDetected,
372 InvalidHandle,
373 NotSupported,
374 Disabled,
375 Unknown,
376};
377
378struct MotionSensorCalibration {
379 s16 offset;
380 s16 scale;
381};
382
383struct MotionCalibration {
384 std::array<MotionSensorCalibration, 3> accelerometer;
385 std::array<MotionSensorCalibration, 3> gyro;
386};
387
388// Basic motion data containing data from the sensors and a timestamp in microseconds
389struct MotionData {
390 float gyro_x{};
391 float gyro_y{};
392 float gyro_z{};
393 float accel_x{};
394 float accel_y{};
395 float accel_z{};
396 u64 delta_timestamp{};
397};
398
399struct JoyStickAxisCalibration {
400 u16 max{1};
401 u16 min{1};
402 u16 center{0};
403};
404
405struct JoyStickCalibration {
406 JoyStickAxisCalibration x;
407 JoyStickAxisCalibration y;
408};
409
410struct RingCalibration {
411 s16 default_value;
412 s16 max_value;
413 s16 min_value;
414};
415
416struct Color {
417 u32 body;
418 u32 buttons;
419 u32 left_grip;
420 u32 right_grip;
421};
422
423struct Battery {
424 union {
425 u8 raw{};
426
427 BitField<0, 4, u8> unknown;
428 BitField<4, 1, u8> charging;
429 BitField<5, 3, u8> status;
430 };
431};
432
433struct VibrationValue {
434 f32 low_amplitude;
435 f32 low_frequency;
436 f32 high_amplitude;
437 f32 high_frequency;
438};
439
440struct JoyconHandle {
441 SDL_hid_device* handle = nullptr;
442 u8 packet_counter{};
443};
444
445struct MCUConfig {
446 MCUCommand command;
447 MCUSubCommand sub_command;
448 MCUMode mode;
449 INSERT_PADDING_BYTES(0x22);
450 u8 crc;
451};
452static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
453
454#pragma pack(push, 1)
455struct InputReportPassive {
456 InputReport report_mode;
457 u16 button_input;
458 u8 stick_state;
459 std::array<u8, 10> unknown_data;
460};
461static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
462
463struct InputReportActive {
464 InputReport report_mode;
465 u8 packet_id;
466 Battery battery_status;
467 std::array<u8, 3> button_input;
468 std::array<u8, 3> left_stick_state;
469 std::array<u8, 3> right_stick_state;
470 u8 vibration_code;
471 std::array<s16, 6 * 2> motion_input;
472 INSERT_PADDING_BYTES(0x2);
473 s16 ring_input;
474};
475static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
476
477struct InputReportNfcIr {
478 InputReport report_mode;
479 u8 packet_id;
480 Battery battery_status;
481 std::array<u8, 3> button_input;
482 std::array<u8, 3> left_stick_state;
483 std::array<u8, 3> right_stick_state;
484 u8 vibration_code;
485 std::array<s16, 6 * 2> motion_input;
486 INSERT_PADDING_BYTES(0x4);
487};
488static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
489#pragma pack(pop)
490
491struct IMUCalibration {
492 std::array<s16, 3> accelerometer_offset;
493 std::array<s16, 3> accelerometer_scale;
494 std::array<s16, 3> gyroscope_offset;
495 std::array<s16, 3> gyroscope_scale;
496};
497static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size");
498
499struct NFCReadBlock {
500 u8 start;
501 u8 end;
502};
503static_assert(sizeof(NFCReadBlock) == 0x2, "NFCReadBlock is an invalid size");
504
505struct NFCReadBlockCommand {
506 u8 block_count{};
507 std::array<NFCReadBlock, 4> blocks{};
508};
509static_assert(sizeof(NFCReadBlockCommand) == 0x9, "NFCReadBlockCommand is an invalid size");
510
511struct NFCReadCommandData {
512 u8 unknown;
513 u8 uuid_length;
514 u8 unknown_2;
515 std::array<u8, 6> uid;
516 NFCTagType tag_type;
517 NFCReadBlockCommand read_block;
518};
519static_assert(sizeof(NFCReadCommandData) == 0x13, "NFCReadCommandData is an invalid size");
520
521struct NFCPollingCommandData {
522 u8 enable_mifare;
523 u8 unknown_1;
524 u8 unknown_2;
525 u8 unknown_3;
526 u8 unknown_4;
527};
528static_assert(sizeof(NFCPollingCommandData) == 0x05, "NFCPollingCommandData is an invalid size");
529
530struct NFCRequestState {
531 MCUSubCommand sub_command;
532 NFCReadCommand command_argument;
533 u8 packet_id;
534 INSERT_PADDING_BYTES(0x1);
535 MCUPacketFlag packet_flag;
536 u8 data_length;
537 union {
538 std::array<u8, 0x1F> raw_data;
539 NFCReadCommandData nfc_read;
540 NFCPollingCommandData nfc_polling;
541 };
542 u8 crc;
543};
544static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size");
545
546struct IrsConfigure {
547 MCUCommand command;
548 MCUSubCommand sub_command;
549 IrsMode irs_mode;
550 IrsFragments number_of_fragments;
551 u16 mcu_major_version;
552 u16 mcu_minor_version;
553 INSERT_PADDING_BYTES(0x1D);
554 u8 crc;
555};
556static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size");
557
558#pragma pack(push, 1)
559struct IrsRegister {
560 IrRegistersAddress address;
561 u8 value;
562};
563static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size");
564
565struct IrsWriteRegisters {
566 MCUCommand command;
567 MCUSubCommand sub_command;
568 u8 number_of_registers;
569 std::array<IrsRegister, 9> registers;
570 INSERT_PADDING_BYTES(0x7);
571 u8 crc;
572};
573static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size");
574#pragma pack(pop)
575
576struct FirmwareVersion {
577 u8 major;
578 u8 minor;
579};
580static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
581
582struct DeviceInfo {
583 FirmwareVersion firmware;
584 MacAddress mac_address;
585};
586static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size");
587
588struct MotionStatus {
589 bool is_enabled;
590 u64 delta_time;
591 GyroSensitivity gyro_sensitivity;
592 AccelerometerSensitivity accelerometer_sensitivity;
593};
594
595struct RingStatus {
596 bool is_enabled;
597 s16 default_value;
598 s16 max_value;
599 s16 min_value;
600};
601
602struct JoyconCallbacks {
603 std::function<void(Battery)> on_battery_data;
604 std::function<void(Color)> on_color_data;
605 std::function<void(int, bool)> on_button_data;
606 std::function<void(int, f32)> on_stick_data;
607 std::function<void(int, const MotionData&)> on_motion_data;
608 std::function<void(f32)> on_ring_data;
609 std::function<void(const std::vector<u8>&)> on_amiibo_data;
610 std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;
611};
612
613} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
new file mode 100644
index 000000000..5c0f71722
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -0,0 +1,400 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/nfc.h"
7
8namespace InputCommon::Joycon {
9
10NfcProtocol::NfcProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult NfcProtocol::EnableNfc() {
14 LOG_INFO(Input, "Enable NFC");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::NFC,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37
38 return result;
39}
40
41DriverResult NfcProtocol::DisableNfc() {
42 LOG_DEBUG(Input, "Disable NFC");
43 ScopedSetBlocking sb(this);
44 DriverResult result{DriverResult::Success};
45
46 if (result == DriverResult::Success) {
47 result = EnableMCU(false);
48 }
49
50 is_enabled = false;
51
52 return result;
53}
54
55DriverResult NfcProtocol::StartNFCPollingMode() {
56 LOG_DEBUG(Input, "Start NFC pooling Mode");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59 TagFoundData tag_data{};
60
61 if (result == DriverResult::Success) {
62 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::NFC);
63 }
64 if (result == DriverResult::Success) {
65 result = WaitUntilNfcIsReady();
66 }
67 if (result == DriverResult::Success) {
68 is_enabled = true;
69 }
70
71 return result;
72}
73
74DriverResult NfcProtocol::ScanAmiibo(std::vector<u8>& data) {
75 LOG_DEBUG(Input, "Start NFC pooling Mode");
76 ScopedSetBlocking sb(this);
77 DriverResult result{DriverResult::Success};
78 TagFoundData tag_data{};
79
80 if (result == DriverResult::Success) {
81 result = StartPolling(tag_data);
82 }
83 if (result == DriverResult::Success) {
84 result = ReadTag(tag_data);
85 }
86 if (result == DriverResult::Success) {
87 result = WaitUntilNfcIsReady();
88 }
89 if (result == DriverResult::Success) {
90 result = StartPolling(tag_data);
91 }
92 if (result == DriverResult::Success) {
93 result = GetAmiiboData(data);
94 }
95
96 return result;
97}
98
99bool NfcProtocol::HasAmiibo() {
100 ScopedSetBlocking sb(this);
101 DriverResult result{DriverResult::Success};
102 TagFoundData tag_data{};
103
104 if (result == DriverResult::Success) {
105 result = StartPolling(tag_data);
106 }
107
108 return result == DriverResult::Success;
109}
110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10;
113 std::vector<u8> output;
114 std::size_t tries = 0;
115
116 do {
117 auto result = SendStartWaitingRecieveRequest(output);
118
119 if (result != DriverResult::Success) {
120 return result;
121 }
122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout;
124 }
125 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 ||
126 output[56] != 0x00);
127
128 return DriverResult::Success;
129}
130
131DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
132 LOG_DEBUG(Input, "Start Polling for tag");
133 constexpr std::size_t timeout_limit = 7;
134 std::vector<u8> output;
135 std::size_t tries = 0;
136
137 do {
138 const auto result = SendStartPollingRequest(output);
139 if (result != DriverResult::Success) {
140 return result;
141 }
142 if (tries++ > timeout_limit) {
143 return DriverResult::Timeout;
144 }
145 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09);
146
147 data.type = output[62];
148 data.uuid.resize(output[64]);
149 memcpy(data.uuid.data(), output.data() + 65, data.uuid.size());
150
151 return DriverResult::Success;
152}
153
154DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
155 constexpr std::size_t timeout_limit = 10;
156 std::vector<u8> output;
157 std::size_t tries = 0;
158
159 std::string uuid_string;
160 for (auto& content : data.uuid) {
161 uuid_string += fmt::format(" {:02x}", content);
162 }
163
164 LOG_INFO(Input, "Tag detected, type={}, uuid={}", data.type, uuid_string);
165
166 tries = 0;
167 NFCPages ntag_pages = NFCPages::Block0;
168 // Read Tag data
169 while (true) {
170 auto result = SendReadAmiiboRequest(output, ntag_pages);
171 const auto mcu_report = static_cast<MCUReport>(output[49]);
172 const auto nfc_status = static_cast<NFCStatus>(output[56]);
173
174 if (result != DriverResult::Success) {
175 return result;
176 }
177
178 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) &&
179 nfc_status == NFCStatus::TagLost) {
180 return DriverResult::ErrorReadingData;
181 }
182
183 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) {
184 if (data.type != 2) {
185 continue;
186 }
187 switch (output[74]) {
188 case 0:
189 ntag_pages = NFCPages::Block135;
190 break;
191 case 3:
192 ntag_pages = NFCPages::Block45;
193 break;
194 case 4:
195 ntag_pages = NFCPages::Block231;
196 break;
197 default:
198 return DriverResult::ErrorReadingData;
199 }
200 continue;
201 }
202
203 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
204 // finished
205 SendStopPollingRequest(output);
206 return DriverResult::Success;
207 }
208
209 // Ignore other state reports
210 if (mcu_report == MCUReport::NFCState) {
211 continue;
212 }
213
214 if (tries++ > timeout_limit) {
215 return DriverResult::Timeout;
216 }
217 }
218
219 return DriverResult::Success;
220}
221
222DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
223 constexpr std::size_t timeout_limit = 10;
224 std::vector<u8> output;
225 std::size_t tries = 0;
226
227 NFCPages ntag_pages = NFCPages::Block135;
228 std::size_t ntag_buffer_pos = 0;
229 // Read Tag data
230 while (true) {
231 auto result = SendReadAmiiboRequest(output, ntag_pages);
232 const auto mcu_report = static_cast<MCUReport>(output[49]);
233 const auto nfc_status = static_cast<NFCStatus>(output[56]);
234
235 if (result != DriverResult::Success) {
236 return result;
237 }
238
239 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) &&
240 nfc_status == NFCStatus::TagLost) {
241 return DriverResult::ErrorReadingData;
242 }
243
244 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) {
245 std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF;
246 if (output[52] == 0x01) {
247 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60);
248 ntag_buffer_pos += payload_size - 60;
249 } else {
250 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size);
251 }
252 continue;
253 }
254
255 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
256 LOG_INFO(Input, "Finished reading amiibo");
257 return DriverResult::Success;
258 }
259
260 // Ignore other state reports
261 if (mcu_report == MCUReport::NFCState) {
262 continue;
263 }
264
265 if (tries++ > timeout_limit) {
266 return DriverResult::Timeout;
267 }
268 }
269
270 return DriverResult::Success;
271}
272
273DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) {
274 NFCRequestState request{
275 .sub_command = MCUSubCommand::ReadDeviceMode,
276 .command_argument = NFCReadCommand::StartPolling,
277 .packet_id = 0x0,
278 .packet_flag = MCUPacketFlag::LastCommandPacket,
279 .data_length = sizeof(NFCPollingCommandData),
280 .nfc_polling =
281 {
282 .enable_mifare = 0x01,
283 .unknown_1 = 0x00,
284 .unknown_2 = 0x00,
285 .unknown_3 = 0x2c,
286 .unknown_4 = 0x01,
287 },
288 .crc = {},
289 };
290
291 std::array<u8, sizeof(NFCRequestState)> request_data{};
292 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
293 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
294 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
295}
296
297DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) {
298 NFCRequestState request{
299 .sub_command = MCUSubCommand::ReadDeviceMode,
300 .command_argument = NFCReadCommand::StopPolling,
301 .packet_id = 0x0,
302 .packet_flag = MCUPacketFlag::LastCommandPacket,
303 .data_length = 0,
304 .raw_data = {},
305 .crc = {},
306 };
307
308 std::array<u8, sizeof(NFCRequestState)> request_data{};
309 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
310 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
311 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
312}
313
314DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) {
315 NFCRequestState request{
316 .sub_command = MCUSubCommand::ReadDeviceMode,
317 .command_argument = NFCReadCommand::StartWaitingRecieve,
318 .packet_id = 0x0,
319 .packet_flag = MCUPacketFlag::LastCommandPacket,
320 .data_length = 0,
321 .raw_data = {},
322 .crc = {},
323 };
324
325 std::vector<u8> request_data(sizeof(NFCRequestState));
326 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
327 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
328 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
329}
330
331DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) {
332 NFCRequestState request{
333 .sub_command = MCUSubCommand::ReadDeviceMode,
334 .command_argument = NFCReadCommand::Ntag,
335 .packet_id = 0x0,
336 .packet_flag = MCUPacketFlag::LastCommandPacket,
337 .data_length = sizeof(NFCReadCommandData),
338 .nfc_read =
339 {
340 .unknown = 0xd0,
341 .uuid_length = 0x07,
342 .unknown_2 = 0x00,
343 .uid = {},
344 .tag_type = NFCTagType::AllTags,
345 .read_block = GetReadBlockCommand(ntag_pages),
346 },
347 .crc = {},
348 };
349
350 std::array<u8, sizeof(NFCRequestState)> request_data{};
351 memcpy(request_data.data(), &request, sizeof(NFCRequestState));
352 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
353 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
354}
355
356NFCReadBlockCommand NfcProtocol::GetReadBlockCommand(NFCPages pages) const {
357 switch (pages) {
358 case NFCPages::Block0:
359 return {
360 .block_count = 1,
361 };
362 case NFCPages::Block45:
363 return {
364 .block_count = 1,
365 .blocks =
366 {
367 NFCReadBlock{0x00, 0x2C},
368 },
369 };
370 case NFCPages::Block135:
371 return {
372 .block_count = 3,
373 .blocks =
374 {
375 NFCReadBlock{0x00, 0x3b},
376 {0x3c, 0x77},
377 {0x78, 0x86},
378 },
379 };
380 case NFCPages::Block231:
381 return {
382 .block_count = 4,
383 .blocks =
384 {
385 NFCReadBlock{0x00, 0x3b},
386 {0x3c, 0x77},
387 {0x78, 0x83},
388 {0xb4, 0xe6},
389 },
390 };
391 default:
392 return {};
393 };
394}
395
396bool NfcProtocol::IsEnabled() const {
397 return is_enabled;
398}
399
400} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
new file mode 100644
index 000000000..e63665aa9
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class NfcProtocol final : private JoyconCommonProtocol {
19public:
20 explicit NfcProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableNfc();
23
24 DriverResult DisableNfc();
25
26 DriverResult StartNFCPollingMode();
27
28 DriverResult ScanAmiibo(std::vector<u8>& data);
29
30 bool HasAmiibo();
31
32 bool IsEnabled() const;
33
34private:
35 struct TagFoundData {
36 u8 type;
37 std::vector<u8> uuid;
38 };
39
40 DriverResult WaitUntilNfcIsReady();
41
42 DriverResult StartPolling(TagFoundData& data);
43
44 DriverResult ReadTag(const TagFoundData& data);
45
46 DriverResult GetAmiiboData(std::vector<u8>& data);
47
48 DriverResult SendStartPollingRequest(std::vector<u8>& output);
49
50 DriverResult SendStopPollingRequest(std::vector<u8>& output);
51
52 DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output);
53
54 DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages);
55
56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
57
58 bool is_enabled{};
59};
60
61} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
new file mode 100644
index 000000000..7f8e093fa
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -0,0 +1,341 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/poller.h"
6
7namespace InputCommon::Joycon {
8
9JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
10 JoyStickCalibration right_stick_calibration_,
11 MotionCalibration motion_calibration_)
12 : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
13 right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
14
15void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
16 callbacks = std::move(callbacks_);
17}
18
19void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
20 const RingStatus& ring_status) {
21 InputReportActive data{};
22 memcpy(&data, buffer.data(), sizeof(InputReportActive));
23
24 switch (device_type) {
25 case Joycon::ControllerType::Left:
26 UpdateActiveLeftPadInput(data, motion_status);
27 break;
28 case Joycon::ControllerType::Right:
29 UpdateActiveRightPadInput(data, motion_status);
30 break;
31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status);
33 break;
34 case Joycon::ControllerType::Grip:
35 case Joycon::ControllerType::Dual:
36 case Joycon::ControllerType::None:
37 break;
38 }
39
40 if (ring_status.is_enabled) {
41 UpdateRing(data.ring_input, ring_status);
42 }
43
44 callbacks.on_battery_data(data.battery_status);
45}
46
47void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
48 InputReportPassive data{};
49 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
50
51 switch (device_type) {
52 case Joycon::ControllerType::Left:
53 UpdatePasiveLeftPadInput(data);
54 break;
55 case Joycon::ControllerType::Right:
56 UpdatePasiveRightPadInput(data);
57 break;
58 case Joycon::ControllerType::Pro:
59 UpdatePasiveProPadInput(data);
60 break;
61 case Joycon::ControllerType::Grip:
62 case Joycon::ControllerType::Dual:
63 case Joycon::ControllerType::None:
64 break;
65 }
66}
67
68void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
69 // This mode is compatible with the active mode
70 ReadActiveMode(buffer, motion_status, {});
71}
72
73void JoyconPoller::UpdateColor(const Color& color) {
74 callbacks.on_color_data(color);
75}
76
77void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
78 callbacks.on_amiibo_data(amiibo_data);
79}
80
81void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
82 callbacks.on_camera_data(camera_data, format);
83}
84
85void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {
86 float normalized_value = static_cast<float>(value - ring_status.default_value);
87 if (normalized_value > 0) {
88 normalized_value = normalized_value /
89 static_cast<float>(ring_status.max_value - ring_status.default_value);
90 }
91 if (normalized_value < 0) {
92 normalized_value = normalized_value /
93 static_cast<float>(ring_status.default_value - ring_status.min_value);
94 }
95 callbacks.on_ring_data(normalized_value);
96}
97
98void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
99 const MotionStatus& motion_status) {
100 static constexpr std::array<Joycon::PadButton, 11> left_buttons{
101 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
102 Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
103 Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
104 Joycon::PadButton::Capture, Joycon::PadButton::StickL,
105 };
106
107 const u32 raw_button =
108 static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
109 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
110 const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
111 const int button = static_cast<int>(left_buttons[i]);
112 callbacks.on_button_data(button, button_status);
113 }
114
115 const u16 raw_left_axis_x =
116 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
117 const u16 raw_left_axis_y =
118 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
119 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
120 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
121 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
122 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
123
124 if (motion_status.is_enabled) {
125 auto left_motion = GetMotionInput(input, motion_status);
126 // Rotate motion axis to the correct direction
127 left_motion.accel_y = -left_motion.accel_y;
128 left_motion.accel_z = -left_motion.accel_z;
129 left_motion.gyro_x = -left_motion.gyro_x;
130 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
131 }
132}
133
134void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
135 const MotionStatus& motion_status) {
136 static constexpr std::array<Joycon::PadButton, 11> right_buttons{
137 Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
138 Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
139 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
140 Joycon::PadButton::Home, Joycon::PadButton::StickR,
141 };
142
143 const u32 raw_button =
144 static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
145 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
146 const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
147 const int button = static_cast<int>(right_buttons[i]);
148 callbacks.on_button_data(button, button_status);
149 }
150
151 const u16 raw_right_axis_x =
152 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
153 const u16 raw_right_axis_y =
154 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
155 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
156 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
157 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
158 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
159
160 if (motion_status.is_enabled) {
161 auto right_motion = GetMotionInput(input, motion_status);
162 // Rotate motion axis to the correct direction
163 right_motion.accel_x = -right_motion.accel_x;
164 right_motion.accel_y = -right_motion.accel_y;
165 right_motion.gyro_z = -right_motion.gyro_z;
166 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
167 }
168}
169
170void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
171 const MotionStatus& motion_status) {
172 static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
173 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
174 Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
175 Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
176 Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
177 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
178 Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
179 };
180
181 const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
182 (input.button_input[1] << 16));
183 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
184 const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
185 const int button = static_cast<int>(pro_buttons[i]);
186 callbacks.on_button_data(button, button_status);
187 }
188
189 const u16 raw_left_axis_x =
190 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
191 const u16 raw_left_axis_y =
192 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
193 const u16 raw_right_axis_x =
194 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
195 const u16 raw_right_axis_y =
196 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
197
198 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
199 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
200 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
201 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
202 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
203 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
204 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
205 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
206
207 if (motion_status.is_enabled) {
208 auto pro_motion = GetMotionInput(input, motion_status);
209 pro_motion.gyro_x = -pro_motion.gyro_x;
210 pro_motion.accel_y = -pro_motion.accel_y;
211 pro_motion.accel_z = -pro_motion.accel_z;
212 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
213 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
214 }
215}
216
217void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
218 static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
219 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
220 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
221 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
222 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
223 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
224 Joycon::PasivePadButton::StickL,
225 };
226
227 for (auto left_button : left_buttons) {
228 const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0;
229 const int button = static_cast<int>(left_button);
230 callbacks.on_button_data(button, button_status);
231 }
232}
233
234void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
235 static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
236 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
237 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
238 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
239 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
240 Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
241 Joycon::PasivePadButton::StickR,
242 };
243
244 for (auto right_button : right_buttons) {
245 const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0;
246 const int button = static_cast<int>(right_button);
247 callbacks.on_button_data(button, button_status);
248 }
249}
250
251void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
252 static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
253 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
254 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
255 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
256 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
257 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
258 Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
259 Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
260 };
261
262 for (auto pro_button : pro_buttons) {
263 const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0;
264 const int button = static_cast<int>(pro_button);
265 callbacks.on_button_data(button, button_status);
266 }
267}
268
269f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
270 const f32 value = static_cast<f32>(raw_value - calibration.center);
271 if (value > 0.0f) {
272 return value / calibration.max;
273 }
274 return value / calibration.min;
275}
276
277f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
278 AccelerometerSensitivity sensitivity) const {
279 const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
280 switch (sensitivity) {
281 case Joycon::AccelerometerSensitivity::G2:
282 return value / 4.0f;
283 case Joycon::AccelerometerSensitivity::G4:
284 return value / 2.0f;
285 case Joycon::AccelerometerSensitivity::G8:
286 return value;
287 case Joycon::AccelerometerSensitivity::G16:
288 return value * 2.0f;
289 }
290 return value;
291}
292
293f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
294 GyroSensitivity sensitivity) const {
295 const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
296 switch (sensitivity) {
297 case Joycon::GyroSensitivity::DPS250:
298 return value / 8.0f;
299 case Joycon::GyroSensitivity::DPS500:
300 return value / 4.0f;
301 case Joycon::GyroSensitivity::DPS1000:
302 return value / 2.0f;
303 case Joycon::GyroSensitivity::DPS2000:
304 return value;
305 }
306 return value;
307}
308
309s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
310 const InputReportActive& input) const {
311 return input.motion_input[(sensor * 3) + axis];
312}
313
314MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
315 const MotionStatus& motion_status) const {
316 MotionData motion{};
317 const auto& accel_cal = motion_calibration.accelerometer;
318 const auto& gyro_cal = motion_calibration.gyro;
319 const s16 raw_accel_x = input.motion_input[1];
320 const s16 raw_accel_y = input.motion_input[0];
321 const s16 raw_accel_z = input.motion_input[2];
322 const s16 raw_gyro_x = input.motion_input[4];
323 const s16 raw_gyro_y = input.motion_input[3];
324 const s16 raw_gyro_z = input.motion_input[5];
325
326 motion.delta_timestamp = motion_status.delta_time;
327 motion.accel_x =
328 GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
329 motion.accel_y =
330 GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
331 motion.accel_z =
332 GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
333 motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
334 motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
335 motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
336
337 // TODO(German77): Return all three samples data
338 return motion;
339}
340
341} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h
new file mode 100644
index 000000000..354d41dad
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.h
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <functional>
12#include <span>
13
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18// Handles input packages and triggers the corresponding input events
19class JoyconPoller {
20public:
21 JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
22 JoyStickCalibration right_stick_calibration_,
23 MotionCalibration motion_calibration_);
24
25 void SetCallbacks(const Joycon::JoyconCallbacks& callbacks_);
26
27 /// Handles data from passive packages
28 void ReadPassiveMode(std::span<u8> buffer);
29
30 /// Handles data from active packages
31 void ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
32 const RingStatus& ring_status);
33
34 /// Handles data from nfc or ir packages
35 void ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status);
36
37 void UpdateColor(const Color& color);
38 void UpdateRing(s16 value, const RingStatus& ring_status);
39 void UpdateAmiibo(const std::vector<u8>& amiibo_data);
40 void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);
41
42private:
43 void UpdateActiveLeftPadInput(const InputReportActive& input,
44 const MotionStatus& motion_status);
45 void UpdateActiveRightPadInput(const InputReportActive& input,
46 const MotionStatus& motion_status);
47 void UpdateActiveProPadInput(const InputReportActive& input, const MotionStatus& motion_status);
48
49 void UpdatePasiveLeftPadInput(const InputReportPassive& buffer);
50 void UpdatePasiveRightPadInput(const InputReportPassive& buffer);
51 void UpdatePasiveProPadInput(const InputReportPassive& buffer);
52
53 /// Returns a calibrated joystick axis from raw axis data
54 f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const;
55
56 /// Returns a calibrated accelerometer axis from raw motion data
57 f32 GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
58 AccelerometerSensitivity sensitivity) const;
59
60 /// Returns a calibrated gyro axis from raw motion data
61 f32 GetGyroValue(s16 raw_value, const MotionSensorCalibration& cal,
62 GyroSensitivity sensitivity) const;
63
64 /// Returns a raw motion value from a buffer
65 s16 GetRawIMUValues(size_t sensor, size_t axis, const InputReportActive& input) const;
66
67 /// Returns motion data from a buffer
68 MotionData GetMotionInput(const InputReportActive& input,
69 const MotionStatus& motion_status) const;
70
71 ControllerType device_type{};
72
73 // Device calibration
74 JoyStickCalibration left_stick_calibration{};
75 JoyStickCalibration right_stick_calibration{};
76 MotionCalibration motion_calibration{};
77
78 Joycon::JoyconCallbacks callbacks{};
79};
80
81} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
new file mode 100644
index 000000000..12f81309e
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -0,0 +1,117 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/ringcon.h"
6
7namespace InputCommon::Joycon {
8
9RingConProtocol::RingConProtocol(std::shared_ptr<JoyconHandle> handle)
10 : JoyconCommonProtocol(std::move(handle)) {}
11
12DriverResult RingConProtocol::EnableRingCon() {
13 LOG_DEBUG(Input, "Enable Ringcon");
14 ScopedSetBlocking sb(this);
15 DriverResult result{DriverResult::Success};
16
17 if (result == DriverResult::Success) {
18 result = SetReportMode(ReportMode::STANDARD_FULL_60HZ);
19 }
20 if (result == DriverResult::Success) {
21 result = EnableMCU(true);
22 }
23 if (result == DriverResult::Success) {
24 const MCUConfig config{
25 .command = MCUCommand::ConfigureMCU,
26 .sub_command = MCUSubCommand::SetDeviceMode,
27 .mode = MCUMode::Standby,
28 .crc = {},
29 };
30 result = ConfigureMCU(config);
31 }
32
33 return result;
34}
35
36DriverResult RingConProtocol::DisableRingCon() {
37 LOG_DEBUG(Input, "Disable RingCon");
38 ScopedSetBlocking sb(this);
39 DriverResult result{DriverResult::Success};
40
41 if (result == DriverResult::Success) {
42 result = EnableMCU(false);
43 }
44
45 is_enabled = false;
46
47 return result;
48}
49
50DriverResult RingConProtocol::StartRingconPolling() {
51 LOG_DEBUG(Input, "Enable Ringcon");
52 ScopedSetBlocking sb(this);
53 DriverResult result{DriverResult::Success};
54 bool is_connected = false;
55
56 if (result == DriverResult::Success) {
57 result = IsRingConnected(is_connected);
58 }
59 if (result == DriverResult::Success && is_connected) {
60 LOG_INFO(Input, "Ringcon detected");
61 result = ConfigureRing();
62 }
63 if (result == DriverResult::Success) {
64 is_enabled = true;
65 }
66
67 return result;
68}
69
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28;
73 constexpr u8 ring_controller_id = 0x20;
74 std::vector<u8> output;
75 std::size_t tries = 0;
76 is_connected = false;
77
78 do {
79 std::array<u8, 1> empty_data{};
80 const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output);
81
82 if (result != DriverResult::Success) {
83 return result;
84 }
85
86 if (tries++ >= max_tries) {
87 return DriverResult::NoDeviceDetected;
88 }
89 } while (output[16] != ring_controller_id);
90
91 is_connected = true;
92 return DriverResult::Success;
93}
94
95DriverResult RingConProtocol::ConfigureRing() {
96 LOG_DEBUG(Input, "ConfigureRing");
97
98 static constexpr std::array<u8, 37> ring_config{
99 0x06, 0x03, 0x25, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x16, 0xED, 0x34, 0x36,
100 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
102
103 const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config);
104
105 if (result != DriverResult::Success) {
106 return result;
107 }
108
109 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
110 return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data);
111}
112
113bool RingConProtocol::IsEnabled() const {
114 return is_enabled;
115}
116
117} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.h b/src/input_common/helpers/joycon_protocol/ringcon.h
new file mode 100644
index 000000000..6e858f3fc
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/ringcon.h
@@ -0,0 +1,38 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RingConProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RingConProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRingCon();
23
24 DriverResult DisableRingCon();
25
26 DriverResult StartRingconPolling();
27
28 bool IsEnabled() const;
29
30private:
31 DriverResult IsRingConnected(bool& is_connected);
32
33 DriverResult ConfigureRing();
34
35 bool is_enabled{};
36};
37
38} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.cpp b/src/input_common/helpers/joycon_protocol/rumble.cpp
new file mode 100644
index 000000000..63b60c946
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.cpp
@@ -0,0 +1,299 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <cmath>
6
7#include "common/logging/log.h"
8#include "input_common/helpers/joycon_protocol/rumble.h"
9
10namespace InputCommon::Joycon {
11
12RumbleProtocol::RumbleProtocol(std::shared_ptr<JoyconHandle> handle)
13 : JoyconCommonProtocol(std::move(handle)) {}
14
15DriverResult RumbleProtocol::EnableRumble(bool is_enabled) {
16 LOG_DEBUG(Input, "Enable Rumble");
17 ScopedSetBlocking sb(this);
18 const std::array<u8, 1> buffer{static_cast<u8>(is_enabled ? 1 : 0)};
19 return SendSubCommand(SubCommand::ENABLE_VIBRATION, buffer);
20}
21
22DriverResult RumbleProtocol::SendVibration(const VibrationValue& vibration) {
23 std::array<u8, sizeof(DefaultVibrationBuffer)> buffer{};
24
25 if (vibration.high_amplitude <= 0.0f && vibration.low_amplitude <= 0.0f) {
26 return SendVibrationReport(DefaultVibrationBuffer);
27 }
28
29 // Protect joycons from damage from strong vibrations
30 const f32 clamp_amplitude =
31 1.0f / std::max(1.0f, vibration.high_amplitude + vibration.low_amplitude);
32
33 const u16 encoded_high_frequency = EncodeHighFrequency(vibration.high_frequency);
34 const u8 encoded_high_amplitude =
35 EncodeHighAmplitude(vibration.high_amplitude * clamp_amplitude);
36 const u8 encoded_low_frequency = EncodeLowFrequency(vibration.low_frequency);
37 const u16 encoded_low_amplitude = EncodeLowAmplitude(vibration.low_amplitude * clamp_amplitude);
38
39 buffer[0] = static_cast<u8>(encoded_high_frequency & 0xFF);
40 buffer[1] = static_cast<u8>(encoded_high_amplitude | ((encoded_high_frequency >> 8) & 0x01));
41 buffer[2] = static_cast<u8>(encoded_low_frequency | ((encoded_low_amplitude >> 8) & 0x80));
42 buffer[3] = static_cast<u8>(encoded_low_amplitude & 0xFF);
43
44 // Duplicate rumble for now
45 buffer[4] = buffer[0];
46 buffer[5] = buffer[1];
47 buffer[6] = buffer[2];
48 buffer[7] = buffer[3];
49
50 return SendVibrationReport(buffer);
51}
52
53u16 RumbleProtocol::EncodeHighFrequency(f32 frequency) const {
54 const u8 new_frequency =
55 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
56 return static_cast<u16>((new_frequency - 0x60) * 4);
57}
58
59u8 RumbleProtocol::EncodeLowFrequency(f32 frequency) const {
60 const u8 new_frequency =
61 static_cast<u8>(std::clamp(std::log2(frequency / 10.0f) * 32.0f, 0.0f, 255.0f));
62 return static_cast<u8>(new_frequency - 0x40);
63}
64
65u8 RumbleProtocol::EncodeHighAmplitude(f32 amplitude) const {
66 // More information about these values can be found here:
67 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
68
69 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
70 std::pair<f32, int>{0.0f, 0x0},
71 {0.01f, 0x2},
72 {0.012f, 0x4},
73 {0.014f, 0x6},
74 {0.017f, 0x8},
75 {0.02f, 0x0a},
76 {0.024f, 0x0c},
77 {0.028f, 0x0e},
78 {0.033f, 0x10},
79 {0.04f, 0x12},
80 {0.047f, 0x14},
81 {0.056f, 0x16},
82 {0.067f, 0x18},
83 {0.08f, 0x1a},
84 {0.095f, 0x1c},
85 {0.112f, 0x1e},
86 {0.117f, 0x20},
87 {0.123f, 0x22},
88 {0.128f, 0x24},
89 {0.134f, 0x26},
90 {0.14f, 0x28},
91 {0.146f, 0x2a},
92 {0.152f, 0x2c},
93 {0.159f, 0x2e},
94 {0.166f, 0x30},
95 {0.173f, 0x32},
96 {0.181f, 0x34},
97 {0.189f, 0x36},
98 {0.198f, 0x38},
99 {0.206f, 0x3a},
100 {0.215f, 0x3c},
101 {0.225f, 0x3e},
102 {0.23f, 0x40},
103 {0.235f, 0x42},
104 {0.24f, 0x44},
105 {0.245f, 0x46},
106 {0.251f, 0x48},
107 {0.256f, 0x4a},
108 {0.262f, 0x4c},
109 {0.268f, 0x4e},
110 {0.273f, 0x50},
111 {0.279f, 0x52},
112 {0.286f, 0x54},
113 {0.292f, 0x56},
114 {0.298f, 0x58},
115 {0.305f, 0x5a},
116 {0.311f, 0x5c},
117 {0.318f, 0x5e},
118 {0.325f, 0x60},
119 {0.332f, 0x62},
120 {0.34f, 0x64},
121 {0.347f, 0x66},
122 {0.355f, 0x68},
123 {0.362f, 0x6a},
124 {0.37f, 0x6c},
125 {0.378f, 0x6e},
126 {0.387f, 0x70},
127 {0.395f, 0x72},
128 {0.404f, 0x74},
129 {0.413f, 0x76},
130 {0.422f, 0x78},
131 {0.431f, 0x7a},
132 {0.44f, 0x7c},
133 {0.45f, 0x7e},
134 {0.46f, 0x80},
135 {0.47f, 0x82},
136 {0.48f, 0x84},
137 {0.491f, 0x86},
138 {0.501f, 0x88},
139 {0.512f, 0x8a},
140 {0.524f, 0x8c},
141 {0.535f, 0x8e},
142 {0.547f, 0x90},
143 {0.559f, 0x92},
144 {0.571f, 0x94},
145 {0.584f, 0x96},
146 {0.596f, 0x98},
147 {0.609f, 0x9a},
148 {0.623f, 0x9c},
149 {0.636f, 0x9e},
150 {0.65f, 0xa0},
151 {0.665f, 0xa2},
152 {0.679f, 0xa4},
153 {0.694f, 0xa6},
154 {0.709f, 0xa8},
155 {0.725f, 0xaa},
156 {0.741f, 0xac},
157 {0.757f, 0xae},
158 {0.773f, 0xb0},
159 {0.79f, 0xb2},
160 {0.808f, 0xb4},
161 {0.825f, 0xb6},
162 {0.843f, 0xb8},
163 {0.862f, 0xba},
164 {0.881f, 0xbc},
165 {0.9f, 0xbe},
166 {0.92f, 0xc0},
167 {0.94f, 0xc2},
168 {0.96f, 0xc4},
169 {0.981f, 0xc6},
170 {1.003f, 0xc8},
171 };
172
173 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
174 if (amplitude <= amplitude_value) {
175 return static_cast<u8>(code);
176 }
177 }
178
179 return static_cast<u8>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
180}
181
182u16 RumbleProtocol::EncodeLowAmplitude(f32 amplitude) const {
183 // More information about these values can be found here:
184 // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md
185
186 static constexpr std::array<std::pair<f32, int>, 101> high_fequency_amplitude{
187 std::pair<f32, int>{0.0f, 0x0040},
188 {0.01f, 0x8040},
189 {0.012f, 0x0041},
190 {0.014f, 0x8041},
191 {0.017f, 0x0042},
192 {0.02f, 0x8042},
193 {0.024f, 0x0043},
194 {0.028f, 0x8043},
195 {0.033f, 0x0044},
196 {0.04f, 0x8044},
197 {0.047f, 0x0045},
198 {0.056f, 0x8045},
199 {0.067f, 0x0046},
200 {0.08f, 0x8046},
201 {0.095f, 0x0047},
202 {0.112f, 0x8047},
203 {0.117f, 0x0048},
204 {0.123f, 0x8048},
205 {0.128f, 0x0049},
206 {0.134f, 0x8049},
207 {0.14f, 0x004a},
208 {0.146f, 0x804a},
209 {0.152f, 0x004b},
210 {0.159f, 0x804b},
211 {0.166f, 0x004c},
212 {0.173f, 0x804c},
213 {0.181f, 0x004d},
214 {0.189f, 0x804d},
215 {0.198f, 0x004e},
216 {0.206f, 0x804e},
217 {0.215f, 0x004f},
218 {0.225f, 0x804f},
219 {0.23f, 0x0050},
220 {0.235f, 0x8050},
221 {0.24f, 0x0051},
222 {0.245f, 0x8051},
223 {0.251f, 0x0052},
224 {0.256f, 0x8052},
225 {0.262f, 0x0053},
226 {0.268f, 0x8053},
227 {0.273f, 0x0054},
228 {0.279f, 0x8054},
229 {0.286f, 0x0055},
230 {0.292f, 0x8055},
231 {0.298f, 0x0056},
232 {0.305f, 0x8056},
233 {0.311f, 0x0057},
234 {0.318f, 0x8057},
235 {0.325f, 0x0058},
236 {0.332f, 0x8058},
237 {0.34f, 0x0059},
238 {0.347f, 0x8059},
239 {0.355f, 0x005a},
240 {0.362f, 0x805a},
241 {0.37f, 0x005b},
242 {0.378f, 0x805b},
243 {0.387f, 0x005c},
244 {0.395f, 0x805c},
245 {0.404f, 0x005d},
246 {0.413f, 0x805d},
247 {0.422f, 0x005e},
248 {0.431f, 0x805e},
249 {0.44f, 0x005f},
250 {0.45f, 0x805f},
251 {0.46f, 0x0060},
252 {0.47f, 0x8060},
253 {0.48f, 0x0061},
254 {0.491f, 0x8061},
255 {0.501f, 0x0062},
256 {0.512f, 0x8062},
257 {0.524f, 0x0063},
258 {0.535f, 0x8063},
259 {0.547f, 0x0064},
260 {0.559f, 0x8064},
261 {0.571f, 0x0065},
262 {0.584f, 0x8065},
263 {0.596f, 0x0066},
264 {0.609f, 0x8066},
265 {0.623f, 0x0067},
266 {0.636f, 0x8067},
267 {0.65f, 0x0068},
268 {0.665f, 0x8068},
269 {0.679f, 0x0069},
270 {0.694f, 0x8069},
271 {0.709f, 0x006a},
272 {0.725f, 0x806a},
273 {0.741f, 0x006b},
274 {0.757f, 0x806b},
275 {0.773f, 0x006c},
276 {0.79f, 0x806c},
277 {0.808f, 0x006d},
278 {0.825f, 0x806d},
279 {0.843f, 0x006e},
280 {0.862f, 0x806e},
281 {0.881f, 0x006f},
282 {0.9f, 0x806f},
283 {0.92f, 0x0070},
284 {0.94f, 0x8070},
285 {0.96f, 0x0071},
286 {0.981f, 0x8071},
287 {1.003f, 0x0072},
288 };
289
290 for (const auto& [amplitude_value, code] : high_fequency_amplitude) {
291 if (amplitude <= amplitude_value) {
292 return static_cast<u16>(code);
293 }
294 }
295
296 return static_cast<u16>(high_fequency_amplitude[high_fequency_amplitude.size() - 1].second);
297}
298
299} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/rumble.h b/src/input_common/helpers/joycon_protocol/rumble.h
new file mode 100644
index 000000000..6c12b7925
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/rumble.h
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse
5// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c
6// https://github.com/CTCaer/jc_toolkit
7// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
8
9#pragma once
10
11#include <vector>
12
13#include "input_common/helpers/joycon_protocol/common_protocol.h"
14#include "input_common/helpers/joycon_protocol/joycon_types.h"
15
16namespace InputCommon::Joycon {
17
18class RumbleProtocol final : private JoyconCommonProtocol {
19public:
20 explicit RumbleProtocol(std::shared_ptr<JoyconHandle> handle);
21
22 DriverResult EnableRumble(bool is_enabled);
23
24 DriverResult SendVibration(const VibrationValue& vibration);
25
26private:
27 u16 EncodeHighFrequency(f32 frequency) const;
28 u8 EncodeLowFrequency(f32 frequency) const;
29 u8 EncodeHighAmplitude(f32 amplitude) const;
30 u16 EncodeLowAmplitude(f32 amplitude) const;
31};
32
33} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index f3a0b3419..096c23b07 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -11,6 +11,11 @@ namespace InputCommon {
11 11
12class Stick final : public Common::Input::InputDevice { 12class Stick final : public Common::Input::InputDevice {
13public: 13public:
14 // Some games such as EARTH DEFENSE FORCE: WORLD BROTHERS
15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18
14 using Button = std::unique_ptr<Common::Input::InputDevice>; 19 using Button = std::unique_ptr<Common::Input::InputDevice>;
15 20
16 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_, 21 Stick(Button up_, Button down_, Button left_, Button right_, Button modifier_, Button updater_,
@@ -196,7 +201,7 @@ public:
196 } 201 }
197 202
198 void UpdateStatus() { 203 void UpdateStatus() {
199 const float coef = modifier_status.value ? modifier_scale : 1.0f; 204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
200 205
201 bool r = right_status; 206 bool r = right_status;
202 bool l = left_status; 207 bool l = left_status;
@@ -290,7 +295,7 @@ public:
290 if (down_status) { 295 if (down_status) {
291 --y; 296 --y;
292 } 297 }
293 const float coef = modifier_status.value ? modifier_scale : 1.0f; 298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
294 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF); 299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
295 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF); 300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
296 return status; 301 return status;
diff --git a/src/input_common/input_engine.cpp b/src/input_common/input_engine.cpp
index 61cfd0911..91aa96aa7 100644
--- a/src/input_common/input_engine.cpp
+++ b/src/input_common/input_engine.cpp
@@ -79,6 +79,17 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
79 TriggerOnBatteryChange(identifier, value); 79 TriggerOnBatteryChange(identifier, value);
80} 80}
81 81
82void InputEngine::SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value) {
83 {
84 std::scoped_lock lock{mutex};
85 ControllerData& controller = controller_list.at(identifier);
86 if (!configuring) {
87 controller.color = value;
88 }
89 }
90 TriggerOnColorChange(identifier, value);
91}
92
82void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) { 93void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
83 { 94 {
84 std::scoped_lock lock{mutex}; 95 std::scoped_lock lock{mutex};
@@ -176,6 +187,18 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
176 return controller.battery; 187 return controller.battery;
177} 188}
178 189
190Common::Input::BodyColorStatus InputEngine::GetColor(const PadIdentifier& identifier) const {
191 std::scoped_lock lock{mutex};
192 const auto controller_iter = controller_list.find(identifier);
193 if (controller_iter == controller_list.cend()) {
194 LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
195 identifier.pad, identifier.port);
196 return {};
197 }
198 const ControllerData& controller = controller_iter->second;
199 return controller.color;
200}
201
179BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const { 202BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
180 std::scoped_lock lock{mutex}; 203 std::scoped_lock lock{mutex};
181 const auto controller_iter = controller_list.find(identifier); 204 const auto controller_iter = controller_list.find(identifier);
@@ -328,6 +351,20 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
328 } 351 }
329} 352}
330 353
354void InputEngine::TriggerOnColorChange(const PadIdentifier& identifier,
355 [[maybe_unused]] Common::Input::BodyColorStatus value) {
356 std::scoped_lock lock{mutex_callback};
357 for (const auto& poller_pair : callback_list) {
358 const InputIdentifier& poller = poller_pair.second;
359 if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Color, 0)) {
360 continue;
361 }
362 if (poller.callback.on_change) {
363 poller.callback.on_change();
364 }
365 }
366}
367
331void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 368void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
332 const BasicMotion& value) { 369 const BasicMotion& value) {
333 std::scoped_lock lock{mutex_callback}; 370 std::scoped_lock lock{mutex_callback};
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index 6cbcf5207..50b5a3dc8 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -40,6 +40,7 @@ enum class EngineInputType {
40 Battery, 40 Battery,
41 Button, 41 Button,
42 Camera, 42 Camera,
43 Color,
43 HatButton, 44 HatButton,
44 Motion, 45 Motion,
45 Nfc, 46 Nfc,
@@ -104,14 +105,17 @@ public:
104 void EndConfiguration(); 105 void EndConfiguration();
105 106
106 // Sets a led pattern for a controller 107 // Sets a led pattern for a controller
107 virtual void SetLeds([[maybe_unused]] const PadIdentifier& identifier, 108 virtual Common::Input::DriverResult SetLeds(
108 [[maybe_unused]] const Common::Input::LedStatus& led_status) {} 109 [[maybe_unused]] const PadIdentifier& identifier,
110 [[maybe_unused]] const Common::Input::LedStatus& led_status) {
111 return Common::Input::DriverResult::NotSupported;
112 }
109 113
110 // Sets rumble to a controller 114 // Sets rumble to a controller
111 virtual Common::Input::VibrationError SetVibration( 115 virtual Common::Input::DriverResult SetVibration(
112 [[maybe_unused]] const PadIdentifier& identifier, 116 [[maybe_unused]] const PadIdentifier& identifier,
113 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) { 117 [[maybe_unused]] const Common::Input::VibrationStatus& vibration) {
114 return Common::Input::VibrationError::NotSupported; 118 return Common::Input::DriverResult::NotSupported;
115 } 119 }
116 120
117 // Returns true if device supports vibrations 121 // Returns true if device supports vibrations
@@ -120,17 +124,17 @@ public:
120 } 124 }
121 125
122 // Sets polling mode to a controller 126 // Sets polling mode to a controller
123 virtual Common::Input::PollingError SetPollingMode( 127 virtual Common::Input::DriverResult SetPollingMode(
124 [[maybe_unused]] const PadIdentifier& identifier, 128 [[maybe_unused]] const PadIdentifier& identifier,
125 [[maybe_unused]] const Common::Input::PollingMode polling_mode) { 129 [[maybe_unused]] const Common::Input::PollingMode polling_mode) {
126 return Common::Input::PollingError::NotSupported; 130 return Common::Input::DriverResult::NotSupported;
127 } 131 }
128 132
129 // Sets camera format to a controller 133 // Sets camera format to a controller
130 virtual Common::Input::CameraError SetCameraFormat( 134 virtual Common::Input::DriverResult SetCameraFormat(
131 [[maybe_unused]] const PadIdentifier& identifier, 135 [[maybe_unused]] const PadIdentifier& identifier,
132 [[maybe_unused]] Common::Input::CameraFormat camera_format) { 136 [[maybe_unused]] Common::Input::CameraFormat camera_format) {
133 return Common::Input::CameraError::NotSupported; 137 return Common::Input::DriverResult::NotSupported;
134 } 138 }
135 139
136 // Returns success if nfc is supported 140 // Returns success if nfc is supported
@@ -199,6 +203,7 @@ public:
199 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const; 203 bool GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const;
200 f32 GetAxis(const PadIdentifier& identifier, int axis) const; 204 f32 GetAxis(const PadIdentifier& identifier, int axis) const;
201 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const; 205 Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
206 Common::Input::BodyColorStatus GetColor(const PadIdentifier& identifier) const;
202 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const; 207 BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
203 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const; 208 Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
204 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const; 209 Common::Input::NfcStatus GetNfc(const PadIdentifier& identifier) const;
@@ -212,6 +217,7 @@ protected:
212 void SetHatButton(const PadIdentifier& identifier, int button, u8 value); 217 void SetHatButton(const PadIdentifier& identifier, int button, u8 value);
213 void SetAxis(const PadIdentifier& identifier, int axis, f32 value); 218 void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
214 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 219 void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
220 void SetColor(const PadIdentifier& identifier, Common::Input::BodyColorStatus value);
215 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value); 221 void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
216 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value); 222 void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
217 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value); 223 void SetNfc(const PadIdentifier& identifier, const Common::Input::NfcStatus& value);
@@ -227,6 +233,7 @@ private:
227 std::unordered_map<int, float> axes; 233 std::unordered_map<int, float> axes;
228 std::unordered_map<int, BasicMotion> motions; 234 std::unordered_map<int, BasicMotion> motions;
229 Common::Input::BatteryLevel battery{}; 235 Common::Input::BatteryLevel battery{};
236 Common::Input::BodyColorStatus color{};
230 Common::Input::CameraStatus camera{}; 237 Common::Input::CameraStatus camera{};
231 Common::Input::NfcStatus nfc{}; 238 Common::Input::NfcStatus nfc{};
232 }; 239 };
@@ -235,6 +242,8 @@ private:
235 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value); 242 void TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value);
236 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value); 243 void TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value);
237 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value); 244 void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
245 void TriggerOnColorChange(const PadIdentifier& identifier,
246 Common::Input::BodyColorStatus value);
238 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion, 247 void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
239 const BasicMotion& value); 248 const BasicMotion& value);
240 void TriggerOnCameraChange(const PadIdentifier& identifier, 249 void TriggerOnCameraChange(const PadIdentifier& identifier,
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index fb8be42e2..15cbf7e5f 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -498,6 +498,58 @@ private:
498 InputEngine* input_engine; 498 InputEngine* input_engine;
499}; 499};
500 500
501class InputFromColor final : public Common::Input::InputDevice {
502public:
503 explicit InputFromColor(PadIdentifier identifier_, InputEngine* input_engine_)
504 : identifier(identifier_), input_engine(input_engine_) {
505 UpdateCallback engine_callback{[this]() { OnChange(); }};
506 const InputIdentifier input_identifier{
507 .identifier = identifier,
508 .type = EngineInputType::Color,
509 .index = 0,
510 .callback = engine_callback,
511 };
512 last_color_value = {};
513 callback_key = input_engine->SetCallback(input_identifier);
514 }
515
516 ~InputFromColor() override {
517 input_engine->DeleteCallback(callback_key);
518 }
519
520 Common::Input::BodyColorStatus GetStatus() const {
521 return input_engine->GetColor(identifier);
522 }
523
524 void ForceUpdate() override {
525 const Common::Input::CallbackStatus status{
526 .type = Common::Input::InputType::Color,
527 .color_status = GetStatus(),
528 };
529
530 last_color_value = status.color_status;
531 TriggerOnChange(status);
532 }
533
534 void OnChange() {
535 const Common::Input::CallbackStatus status{
536 .type = Common::Input::InputType::Color,
537 .color_status = GetStatus(),
538 };
539
540 if (status.color_status.body != last_color_value.body) {
541 last_color_value = status.color_status;
542 TriggerOnChange(status);
543 }
544 }
545
546private:
547 const PadIdentifier identifier;
548 int callback_key;
549 Common::Input::BodyColorStatus last_color_value;
550 InputEngine* input_engine;
551};
552
501class InputFromMotion final : public Common::Input::InputDevice { 553class InputFromMotion final : public Common::Input::InputDevice {
502public: 554public:
503 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_, 555 explicit InputFromMotion(PadIdentifier identifier_, int motion_sensor_, float gyro_threshold_,
@@ -754,11 +806,11 @@ public:
754 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_) 806 explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
755 : identifier(identifier_), input_engine(input_engine_) {} 807 : identifier(identifier_), input_engine(input_engine_) {}
756 808
757 void SetLED(const Common::Input::LedStatus& led_status) override { 809 Common::Input::DriverResult SetLED(const Common::Input::LedStatus& led_status) override {
758 input_engine->SetLeds(identifier, led_status); 810 return input_engine->SetLeds(identifier, led_status);
759 } 811 }
760 812
761 Common::Input::VibrationError SetVibration( 813 Common::Input::DriverResult SetVibration(
762 const Common::Input::VibrationStatus& vibration_status) override { 814 const Common::Input::VibrationStatus& vibration_status) override {
763 return input_engine->SetVibration(identifier, vibration_status); 815 return input_engine->SetVibration(identifier, vibration_status);
764 } 816 }
@@ -767,11 +819,12 @@ public:
767 return input_engine->IsVibrationEnabled(identifier); 819 return input_engine->IsVibrationEnabled(identifier);
768 } 820 }
769 821
770 Common::Input::PollingError SetPollingMode(Common::Input::PollingMode polling_mode) override { 822 Common::Input::DriverResult SetPollingMode(Common::Input::PollingMode polling_mode) override {
771 return input_engine->SetPollingMode(identifier, polling_mode); 823 return input_engine->SetPollingMode(identifier, polling_mode);
772 } 824 }
773 825
774 Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override { 826 Common::Input::DriverResult SetCameraFormat(
827 Common::Input::CameraFormat camera_format) override {
775 return input_engine->SetCameraFormat(identifier, camera_format); 828 return input_engine->SetCameraFormat(identifier, camera_format);
776 } 829 }
777 830
@@ -966,6 +1019,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
966 return std::make_unique<InputFromBattery>(identifier, input_engine.get()); 1019 return std::make_unique<InputFromBattery>(identifier, input_engine.get());
967} 1020}
968 1021
1022std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateColorDevice(
1023 const Common::ParamPackage& params) {
1024 const PadIdentifier identifier = {
1025 .guid = Common::UUID{params.Get("guid", "")},
1026 .port = static_cast<std::size_t>(params.Get("port", 0)),
1027 .pad = static_cast<std::size_t>(params.Get("pad", 0)),
1028 };
1029
1030 input_engine->PreSetController(identifier);
1031 return std::make_unique<InputFromColor>(identifier, input_engine.get());
1032}
1033
969std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice( 1034std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
970 Common::ParamPackage params) { 1035 Common::ParamPackage params) {
971 const PadIdentifier identifier = { 1036 const PadIdentifier identifier = {
@@ -1053,6 +1118,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
1053 if (params.Has("battery")) { 1118 if (params.Has("battery")) {
1054 return CreateBatteryDevice(params); 1119 return CreateBatteryDevice(params);
1055 } 1120 }
1121 if (params.Has("color")) {
1122 return CreateColorDevice(params);
1123 }
1056 if (params.Has("camera")) { 1124 if (params.Has("camera")) {
1057 return CreateCameraDevice(params); 1125 return CreateCameraDevice(params);
1058 } 1126 }
diff --git a/src/input_common/input_poller.h b/src/input_common/input_poller.h
index d7db13ce4..e097e254c 100644
--- a/src/input_common/input_poller.h
+++ b/src/input_common/input_poller.h
@@ -191,6 +191,17 @@ private:
191 const Common::ParamPackage& params); 191 const Common::ParamPackage& params);
192 192
193 /** 193 /**
194 * Creates a color device from the parameters given.
195 * @param params contains parameters for creating the device:
196 * - "guid": text string for identifying controllers
197 * - "port": port of the connected device
198 * - "pad": slot of the connected controller
199 * @returns a unique input device with the parameters specified
200 */
201 std::unique_ptr<Common::Input::InputDevice> CreateColorDevice(
202 const Common::ParamPackage& params);
203
204 /**
194 * Creates a motion device from the parameters given. 205 * Creates a motion device from the parameters given.
195 * @param params contains parameters for creating the device: 206 * @param params contains parameters for creating the device:
196 * - "axis_x": the controller horizontal axis id to bind with the input 207 * - "axis_x": the controller horizontal axis id to bind with the input
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index e0b2131ed..c77fc04ee 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -23,6 +23,7 @@
23#include "input_common/drivers/gc_adapter.h" 23#include "input_common/drivers/gc_adapter.h"
24#endif 24#endif
25#ifdef HAVE_SDL2 25#ifdef HAVE_SDL2
26#include "input_common/drivers/joycon.h"
26#include "input_common/drivers/sdl_driver.h" 27#include "input_common/drivers/sdl_driver.h"
27#endif 28#endif
28 29
@@ -81,6 +82,7 @@ struct InputSubsystem::Impl {
81 RegisterEngine("virtual_gamepad", virtual_gamepad); 82 RegisterEngine("virtual_gamepad", virtual_gamepad);
82#ifdef HAVE_SDL2 83#ifdef HAVE_SDL2
83 RegisterEngine("sdl", sdl); 84 RegisterEngine("sdl", sdl);
85 RegisterEngine("joycon", joycon);
84#endif 86#endif
85 87
86 Common::Input::RegisterInputFactory("touch_from_button", 88 Common::Input::RegisterInputFactory("touch_from_button",
@@ -111,6 +113,7 @@ struct InputSubsystem::Impl {
111 UnregisterEngine(virtual_gamepad); 113 UnregisterEngine(virtual_gamepad);
112#ifdef HAVE_SDL2 114#ifdef HAVE_SDL2
113 UnregisterEngine(sdl); 115 UnregisterEngine(sdl);
116 UnregisterEngine(joycon);
114#endif 117#endif
115 118
116 Common::Input::UnregisterInputFactory("touch_from_button"); 119 Common::Input::UnregisterInputFactory("touch_from_button");
@@ -133,6 +136,8 @@ struct InputSubsystem::Impl {
133 auto udp_devices = udp_client->GetInputDevices(); 136 auto udp_devices = udp_client->GetInputDevices();
134 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end()); 137 devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
135#ifdef HAVE_SDL2 138#ifdef HAVE_SDL2
139 auto joycon_devices = joycon->GetInputDevices();
140 devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
136 auto sdl_devices = sdl->GetInputDevices(); 141 auto sdl_devices = sdl->GetInputDevices();
137 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end()); 142 devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
138#endif 143#endif
@@ -164,6 +169,9 @@ struct InputSubsystem::Impl {
164 if (engine == sdl->GetEngineName()) { 169 if (engine == sdl->GetEngineName()) {
165 return sdl; 170 return sdl;
166 } 171 }
172 if (engine == joycon->GetEngineName()) {
173 return joycon;
174 }
167#endif 175#endif
168 return nullptr; 176 return nullptr;
169 } 177 }
@@ -247,6 +255,9 @@ struct InputSubsystem::Impl {
247 if (engine == sdl->GetEngineName()) { 255 if (engine == sdl->GetEngineName()) {
248 return true; 256 return true;
249 } 257 }
258 if (engine == joycon->GetEngineName()) {
259 return true;
260 }
250#endif 261#endif
251 return false; 262 return false;
252 } 263 }
@@ -260,6 +271,7 @@ struct InputSubsystem::Impl {
260 udp_client->BeginConfiguration(); 271 udp_client->BeginConfiguration();
261#ifdef HAVE_SDL2 272#ifdef HAVE_SDL2
262 sdl->BeginConfiguration(); 273 sdl->BeginConfiguration();
274 joycon->BeginConfiguration();
263#endif 275#endif
264 } 276 }
265 277
@@ -272,6 +284,7 @@ struct InputSubsystem::Impl {
272 udp_client->EndConfiguration(); 284 udp_client->EndConfiguration();
273#ifdef HAVE_SDL2 285#ifdef HAVE_SDL2
274 sdl->EndConfiguration(); 286 sdl->EndConfiguration();
287 joycon->EndConfiguration();
275#endif 288#endif
276 } 289 }
277 290
@@ -304,6 +317,7 @@ struct InputSubsystem::Impl {
304 317
305#ifdef HAVE_SDL2 318#ifdef HAVE_SDL2
306 std::shared_ptr<SDLDriver> sdl; 319 std::shared_ptr<SDLDriver> sdl;
320 std::shared_ptr<Joycons> joycon;
307#endif 321#endif
308}; 322};
309 323
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index fb5799c42..c898ce12f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -436,6 +436,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
436 if (info.type == TextureType::Buffer) { 436 if (info.type == TextureType::Buffer) {
437 lod = Id{}; 437 lod = Id{};
438 } 438 }
439 if (Sirit::ValidId(ms)) {
440 // This image is multisampled, lod must be implicit
441 lod = Id{};
442 }
439 const ImageOperands operands(offset, lod, ms); 443 const ImageOperands operands(offset, lod, ms);
440 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 444 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
441 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 445 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index a0c155fdb..3b97721e1 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
35 const spv::ImageFormat format{spv::ImageFormat::Unknown}; 35 const spv::ImageFormat format{spv::ImageFormat::Unknown};
36 const Id type{ctx.F32[1]}; 36 const Id type{ctx.F32[1]};
37 const bool depth{desc.is_depth}; 37 const bool depth{desc.is_depth};
38 const bool ms{desc.is_multisample};
38 switch (desc.type) { 39 switch (desc.type) {
39 case TextureType::Color1D: 40 case TextureType::Color1D:
40 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); 41 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
42 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); 43 return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
43 case TextureType::Color2D: 44 case TextureType::Color2D:
44 case TextureType::Color2DRect: 45 case TextureType::Color2DRect:
45 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); 46 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
46 case TextureType::ColorArray2D: 47 case TextureType::ColorArray2D:
47 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); 48 return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
48 case TextureType::Color3D: 49 case TextureType::Color3D:
49 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); 50 return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
50 case TextureType::ColorCube: 51 case TextureType::ColorCube:
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index f5c86fcb1..9718c6921 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -524,6 +524,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
524 524
525 const auto& cbuf{texture_inst.cbuf}; 525 const auto& cbuf{texture_inst.cbuf};
526 auto flags{inst->Flags<IR::TextureInstInfo>()}; 526 auto flags{inst->Flags<IR::TextureInstInfo>()};
527 bool is_multisample{false};
527 switch (inst->GetOpcode()) { 528 switch (inst->GetOpcode()) {
528 case IR::Opcode::ImageQueryDimensions: 529 case IR::Opcode::ImageQueryDimensions:
529 flags.type.Assign(ReadTextureType(env, cbuf)); 530 flags.type.Assign(ReadTextureType(env, cbuf));
@@ -538,6 +539,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
538 } 539 }
539 break; 540 break;
540 case IR::Opcode::ImageFetch: 541 case IR::Opcode::ImageFetch:
542 if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
543 flags.type == TextureType::ColorArray2D) {
544 is_multisample = !inst->Arg(4).IsEmpty();
545 } else {
546 inst->SetArg(4, IR::U32{});
547 }
541 if (flags.type != TextureType::Color1D) { 548 if (flags.type != TextureType::Color1D) {
542 break; 549 break;
543 } 550 }
@@ -613,6 +620,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
613 index = descriptors.Add(TextureDescriptor{ 620 index = descriptors.Add(TextureDescriptor{
614 .type = flags.type, 621 .type = flags.type,
615 .is_depth = flags.is_depth != 0, 622 .is_depth = flags.is_depth != 0,
623 .is_multisample = is_multisample,
616 .has_secondary = cbuf.has_secondary, 624 .has_secondary = cbuf.has_secondary,
617 .cbuf_index = cbuf.index, 625 .cbuf_index = cbuf.index,
618 .cbuf_offset = cbuf.offset, 626 .cbuf_offset = cbuf.offset,
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f93181e1e..d308db942 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
109struct TextureDescriptor { 109struct TextureDescriptor {
110 TextureType type; 110 TextureType type;
111 bool is_depth; 111 bool is_depth;
112 bool is_multisample;
112 bool has_secondary; 113 bool has_secondary;
113 u32 cbuf_index; 114 u32 cbuf_index;
114 u32 cbuf_offset; 115 u32 cbuf_offset;
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 9b65e79cb..ae84408bc 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -14,7 +14,6 @@ add_executable(tests
14 core/core_timing.cpp 14 core/core_timing.cpp
15 core/internal_network/network.cpp 15 core/internal_network/network.cpp
16 precompiled_headers.h 16 precompiled_headers.h
17 tests.cpp
18 video_core/buffer_base.cpp 17 video_core/buffer_base.cpp
19 input_common/calibration_configuration_job.cpp 18 input_common/calibration_configuration_job.cpp
20) 19)
@@ -22,7 +21,7 @@ add_executable(tests
22create_target_directory_groups(tests) 21create_target_directory_groups(tests)
23 22
24target_link_libraries(tests PRIVATE common core input_common) 23target_link_libraries(tests PRIVATE common core input_common)
25target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2 Threads::Threads) 24target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain Threads::Threads)
26 25
27add_test(NAME tests COMMAND tests) 26add_test(NAME tests COMMAND tests)
28 27
diff --git a/src/tests/common/bit_field.cpp b/src/tests/common/bit_field.cpp
index 0071ae52e..75e990ecd 100644
--- a/src/tests/common/bit_field.cpp
+++ b/src/tests/common/bit_field.cpp
@@ -4,7 +4,7 @@
4#include <array> 4#include <array>
5#include <cstring> 5#include <cstring>
6#include <type_traits> 6#include <type_traits>
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8#include "common/bit_field.h" 8#include "common/bit_field.h"
9 9
10TEST_CASE("BitField", "[common]") { 10TEST_CASE("BitField", "[common]") {
diff --git a/src/tests/common/cityhash.cpp b/src/tests/common/cityhash.cpp
index 05942eadb..2a391dff1 100644
--- a/src/tests/common/cityhash.cpp
+++ b/src/tests/common/cityhash.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/cityhash.h" 6#include "common/cityhash.h"
7 7
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4e29f9199..ecad7583f 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -11,7 +11,7 @@
11#include <unordered_map> 11#include <unordered_map>
12#include <vector> 12#include <vector>
13 13
14#include <catch2/catch.hpp> 14#include <catch2/catch_test_macros.hpp>
15 15
16#include "common/common_types.h" 16#include "common/common_types.h"
17#include "common/fiber.h" 17#include "common/fiber.h"
diff --git a/src/tests/common/host_memory.cpp b/src/tests/common/host_memory.cpp
index e49d0a09f..1b014b632 100644
--- a/src/tests/common/host_memory.cpp
+++ b/src/tests/common/host_memory.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "common/host_memory.h" 6#include "common/host_memory.h"
7#include "common/literals.h" 7#include "common/literals.h"
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index d036cc83a..41575def4 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2017 Citra Emulator Project 1// SPDX-FileCopyrightText: 2017 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5#include <math.h> 5#include <math.h>
6#include "common/logging/backend.h" 6#include "common/logging/backend.h"
7#include "common/param_package.h" 7#include "common/param_package.h"
diff --git a/src/tests/common/range_map.cpp b/src/tests/common/range_map.cpp
index 5a4630a38..d301ac5f6 100644
--- a/src/tests/common/range_map.cpp
+++ b/src/tests/common/range_map.cpp
@@ -3,7 +3,7 @@
3 3
4#include <stdexcept> 4#include <stdexcept>
5 5
6#include <catch2/catch.hpp> 6#include <catch2/catch_test_macros.hpp>
7 7
8#include "common/range_map.h" 8#include "common/range_map.h"
9 9
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp
index 4f81b6e5e..7dee988c8 100644
--- a/src/tests/common/ring_buffer.cpp
+++ b/src/tests/common/ring_buffer.cpp
@@ -7,7 +7,7 @@
7#include <numeric> 7#include <numeric>
8#include <thread> 8#include <thread>
9#include <vector> 9#include <vector>
10#include <catch2/catch.hpp> 10#include <catch2/catch_test_macros.hpp>
11#include "common/ring_buffer.h" 11#include "common/ring_buffer.h"
12 12
13namespace Common { 13namespace Common {
diff --git a/src/tests/common/scratch_buffer.cpp b/src/tests/common/scratch_buffer.cpp
index f6e50da4a..132f139fa 100644
--- a/src/tests/common/scratch_buffer.cpp
+++ b/src/tests/common/scratch_buffer.cpp
@@ -5,7 +5,7 @@
5#include <array> 5#include <array>
6#include <cstring> 6#include <cstring>
7#include <span> 7#include <span>
8#include <catch2/catch.hpp> 8#include <catch2/catch_test_macros.hpp>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/scratch_buffer.h" 10#include "common/scratch_buffer.h"
11 11
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
index 311272506..f7a23e876 100644
--- a/src/tests/common/unique_function.cpp
+++ b/src/tests/common/unique_function.cpp
@@ -3,7 +3,7 @@
3 3
4#include <string> 4#include <string>
5 5
6#include <catch2/catch.hpp> 6#include <catch2/catch_test_macros.hpp>
7 7
8#include "common/unique_function.h" 8#include "common/unique_function.h"
9 9
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 284b2ae66..f08afbf9a 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project 1// SPDX-FileCopyrightText: 2016 Dolphin Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
diff --git a/src/tests/core/internal_network/network.cpp b/src/tests/core/internal_network/network.cpp
index 164b0ff24..10ddd8b42 100644
--- a/src/tests/core/internal_network/network.cpp
+++ b/src/tests/core/internal_network/network.cpp
@@ -1,7 +1,7 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <catch2/catch.hpp> 4#include <catch2/catch_test_macros.hpp>
5 5
6#include "core/internal_network/network.h" 6#include "core/internal_network/network.h"
7#include "core/internal_network/sockets.h" 7#include "core/internal_network/sockets.h"
diff --git a/src/tests/input_common/calibration_configuration_job.cpp b/src/tests/input_common/calibration_configuration_job.cpp
index e5f698886..516ff1b30 100644
--- a/src/tests/input_common/calibration_configuration_job.cpp
+++ b/src/tests/input_common/calibration_configuration_job.cpp
@@ -6,7 +6,7 @@
6#include <thread> 6#include <thread>
7#include <boost/asio.hpp> 7#include <boost/asio.hpp>
8#include <boost/crc.hpp> 8#include <boost/crc.hpp>
9#include <catch2/catch.hpp> 9#include <catch2/catch_test_macros.hpp>
10 10
11#include "input_common/drivers/udp_client.h" 11#include "input_common/drivers/udp_client.h"
12#include "input_common/helpers/udp_protocol.h" 12#include "input_common/helpers/udp_protocol.h"
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
deleted file mode 100644
index 3f905c05c..000000000
--- a/src/tests/tests.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
1// SPDX-FileCopyrightText: 2016 Citra Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#define CATCH_CONFIG_MAIN
5#include <catch2/catch.hpp>
6
7// Catch provides the main function since we've given it the
8// CATCH_CONFIG_MAIN preprocessor directive.
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 5cd0628f2..1275cca24 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -4,7 +4,7 @@
4#include <stdexcept> 4#include <stdexcept>
5#include <unordered_map> 5#include <unordered_map>
6 6
7#include <catch2/catch.hpp> 7#include <catch2/catch_test_macros.hpp>
8 8
9#include "common/alignment.h" 9#include "common/alignment.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 3bcae3503..83924475b 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -6,7 +6,6 @@
6#include "common/alignment.h" 6#include "common/alignment.h"
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/device_memory.h" 10#include "core/device_memory.h"
12#include "core/hle/kernel/k_page_table.h" 11#include "core/hle/kernel/k_page_table.h"
@@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64
46 big_page_table_cpu.resize(big_page_table_size); 45 big_page_table_cpu.resize(big_page_table_size);
47 big_page_continous.resize(big_page_table_size / continous_bits, 0); 46 big_page_continous.resize(big_page_table_size / continous_bits, 0);
48 entries.resize(page_table_size / 32, 0); 47 entries.resize(page_table_size / 32, 0);
49 if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) {
50 fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer();
51 } else {
52 fastmem_arena = nullptr;
53 }
54} 48}
55 49
56MemoryManager::~MemoryManager() = default; 50MemoryManager::~MemoryManager() = default;
@@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si
360 } 354 }
361} 355}
362 356
363template <bool is_safe, bool use_fastmem> 357template <bool is_safe>
364void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 358void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
365 [[maybe_unused]] VideoCommon::CacheType which) const { 359 [[maybe_unused]] VideoCommon::CacheType which) const {
366 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, 360 auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index,
@@ -374,12 +368,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
374 if constexpr (is_safe) { 368 if constexpr (is_safe) {
375 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 369 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
376 } 370 }
377 if constexpr (use_fastmem) { 371 u8* physical = memory.GetPointer(cpu_addr_base);
378 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 372 std::memcpy(dest_buffer, physical, copy_amount);
379 } else {
380 u8* physical = memory.GetPointer(cpu_addr_base);
381 std::memcpy(dest_buffer, physical, copy_amount);
382 }
383 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 373 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
384 }; 374 };
385 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { 375 auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) {
@@ -388,15 +378,11 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
388 if constexpr (is_safe) { 378 if constexpr (is_safe) {
389 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which); 379 rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);
390 } 380 }
391 if constexpr (use_fastmem) { 381 if (!IsBigPageContinous(page_index)) [[unlikely]] {
392 std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); 382 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);
393 } else { 383 } else {
394 if (!IsBigPageContinous(page_index)) [[unlikely]] { 384 u8* physical = memory.GetPointer(cpu_addr_base);
395 memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); 385 std::memcpy(dest_buffer, physical, copy_amount);
396 } else {
397 u8* physical = memory.GetPointer(cpu_addr_base);
398 std::memcpy(dest_buffer, physical, copy_amount);
399 }
400 } 386 }
401 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount; 387 dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;
402 }; 388 };
@@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:
410 396
411void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 397void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
412 VideoCommon::CacheType which) const { 398 VideoCommon::CacheType which) const {
413 if (fastmem_arena) [[likely]] { 399 ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);
414 ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which);
415 return;
416 }
417 ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which);
418} 400}
419 401
420void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer, 402void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,
421 const std::size_t size) const { 403 const std::size_t size) const {
422 if (fastmem_arena) [[likely]] { 404 ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
423 ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
424 return;
425 }
426 ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);
427} 405}
428 406
429template <bool is_safe> 407template <bool is_safe>
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 2936364f0..9ebfb6179 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -141,7 +141,7 @@ private:
141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, 141 inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,
142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; 142 FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const;
143 143
144 template <bool is_safe, bool use_fastmem> 144 template <bool is_safe>
145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size, 145 void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,
146 VideoCommon::CacheType which) const; 146 VideoCommon::CacheType which) const;
147 147
@@ -215,7 +215,6 @@ private:
215 215
216 std::vector<u64> big_page_continous; 216 std::vector<u64> big_page_continous;
217 std::vector<std::pair<VAddr, std::size_t>> page_stash{}; 217 std::vector<std::pair<VAddr, std::size_t>> page_stash{};
218 u8* fastmem_arena{};
219 218
220 constexpr static size_t continous_bits = 64; 219 constexpr static size_t continous_bits = 64;
221 220
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
index 85f1d13e0..5fa0d9620 100644
--- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
+++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp
@@ -57,7 +57,7 @@ NsightAftermathTracker::NsightAftermathTracker() {
57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps( 57 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_EnableGpuCrashDumps(
58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan, 58 GFSDK_Aftermath_Version_API, GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback, 59 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default, GpuCrashDumpCallback,
60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, this))) { 60 ShaderDebugInfoCallback, CrashDumpDescriptionCallback, nullptr, this))) {
61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); 61 LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed");
62 return; 62 return;
63 } 63 }
@@ -83,7 +83,7 @@ void NsightAftermathTracker::SaveShader(std::span<const u32> spirv) const {
83 83
84 std::scoped_lock lock{mutex}; 84 std::scoped_lock lock{mutex};
85 85
86 GFSDK_Aftermath_ShaderHash hash; 86 GFSDK_Aftermath_ShaderBinaryHash hash;
87 if (!GFSDK_Aftermath_SUCCEED( 87 if (!GFSDK_Aftermath_SUCCEED(
88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) { 88 GFSDK_Aftermath_GetShaderHashSpirv(GFSDK_Aftermath_Version_API, &shader, &hash))) {
89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module"); 89 LOG_ERROR(Render_Vulkan, "Failed to hash SPIR-V module");
@@ -121,8 +121,8 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump,
121 u32 json_size = 0; 121 u32 json_size = 0;
122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON( 122 if (!GFSDK_Aftermath_SUCCEED(GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO, 123 decoder, GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, nullptr, 124 GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE, nullptr, nullptr, nullptr, this,
125 this, &json_size))) { 125 &json_size))) {
126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON"); 126 LOG_ERROR(Render_Vulkan, "Failed to generate JSON");
127 return; 127 return;
128 } 128 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 0db62baa3..35fef506a 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -70,28 +70,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{
70// UISetting::values.shortcuts, which is alphabetically ordered. 70// UISetting::values.shortcuts, which is alphabetically ordered.
71// clang-format off 71// clang-format off
72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ 72const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, 73 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}},
74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, 74 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}},
75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, 75 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}},
76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, 76 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}},
77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, 77 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}},
78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, 78 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}},
79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, 79 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}},
80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, 80 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}},
81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, 81 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}},
82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, 82 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}},
83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, 83 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, 84 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, 85 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, 86 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, 87 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, 88 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, 89 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, 90 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, 91 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}},
92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, 92 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}},
93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, 93 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, 94 {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}},
95}}; 95}};
96// clang-format on 96// clang-format on
97 97
@@ -440,6 +440,7 @@ void Config::ReadControlValues() {
440 ReadBasicSetting(Settings::values.emulate_analog_keyboard); 440 ReadBasicSetting(Settings::values.emulate_analog_keyboard);
441 Settings::values.mouse_panning = false; 441 Settings::values.mouse_panning = false;
442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity); 442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
443 ReadBasicSetting(Settings::values.enable_joycon_driver);
443 444
444 ReadBasicSetting(Settings::values.tas_enable); 445 ReadBasicSetting(Settings::values.tas_enable);
445 ReadBasicSetting(Settings::values.tas_loop); 446 ReadBasicSetting(Settings::values.tas_loop);
@@ -747,7 +748,7 @@ void Config::ReadShortcutValues() {
747 for (const auto& [name, group, shortcut] : default_hotkeys) { 748 for (const auto& [name, group, shortcut] : default_hotkeys) {
748 qt_config->beginGroup(group); 749 qt_config->beginGroup(group);
749 qt_config->beginGroup(name); 750 qt_config->beginGroup(name);
750 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1 751 // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1
751 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open 752 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
752 // a file dialog in windowed mode 753 // a file dialog in windowed mode
753 UISettings::values.shortcuts.push_back( 754 UISettings::values.shortcuts.push_back(
@@ -756,7 +757,7 @@ void Config::ReadShortcutValues() {
756 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), 757 {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(),
757 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) 758 ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq)
758 .toString(), 759 .toString(),
759 shortcut.context}}); 760 shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}});
760 qt_config->endGroup(); 761 qt_config->endGroup();
761 qt_config->endGroup(); 762 qt_config->endGroup();
762 } 763 }
@@ -1139,6 +1140,7 @@ void Config::SaveControlValues() {
1139 WriteGlobalSetting(Settings::values.enable_accurate_vibrations); 1140 WriteGlobalSetting(Settings::values.enable_accurate_vibrations);
1140 WriteGlobalSetting(Settings::values.motion_enabled); 1141 WriteGlobalSetting(Settings::values.motion_enabled);
1141 WriteBasicSetting(Settings::values.enable_raw_input); 1142 WriteBasicSetting(Settings::values.enable_raw_input);
1143 WriteBasicSetting(Settings::values.enable_joycon_driver);
1142 WriteBasicSetting(Settings::values.keyboard_enabled); 1144 WriteBasicSetting(Settings::values.keyboard_enabled);
1143 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1145 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1144 WriteBasicSetting(Settings::values.mouse_panning_sensitivity); 1146 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
@@ -1393,6 +1395,7 @@ void Config::SaveShortcutValues() {
1393 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, 1395 WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq,
1394 default_hotkey.controller_keyseq); 1396 default_hotkey.controller_keyseq);
1395 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); 1397 WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context);
1398 WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat);
1396 qt_config->endGroup(); 1399 qt_config->endGroup();
1397 qt_config->endGroup(); 1400 qt_config->endGroup();
1398 } 1401 }
diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp
index 97fb664bf..ac42cc7fc 100644
--- a/src/yuzu/configuration/configuration_shared.cpp
+++ b/src/yuzu/configuration/configuration_shared.cpp
@@ -92,3 +92,13 @@ void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index
92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); 92 combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); 93 combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
94} 94}
95
96int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
97 if (Settings::IsConfiguringGlobal()) {
98 return combobox->currentIndex();
99 }
100 if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
101 return global_setting_index;
102 }
103 return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
104}
diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h
index e597dcdb5..04c88758c 100644
--- a/src/yuzu/configuration/configuration_shared.h
+++ b/src/yuzu/configuration/configuration_shared.h
@@ -69,4 +69,7 @@ void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global);
69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox 69// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox
70void InsertGlobalItem(QComboBox* combobox, int global_index); 70void InsertGlobalItem(QComboBox* combobox, int global_index);
71 71
72// Returns the correct index of a QComboBox taking into account global configuration
73int GetComboboxIndex(int global_setting_index, const QComboBox* combobox);
74
72} // namespace ConfigurationShared 75} // namespace ConfigurationShared
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4301313cf..2aaefcc05 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,
66 66
67 web_tab->SetWebServiceConfigEnabled(enable_web_config); 67 web_tab->SetWebServiceConfigEnabled(enable_web_config);
68 hotkeys_tab->Populate(registry); 68 hotkeys_tab->Populate(registry);
69 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
70 69
71 input_tab->Initialize(input_subsystem); 70 input_tab->Initialize(input_subsystem);
72 71
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 235b813d9..77b976e74 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -138,6 +138,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
138 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); 139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); 140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
141 Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
141} 142}
142 143
143void ConfigureInputAdvanced::LoadConfiguration() { 144void ConfigureInputAdvanced::LoadConfiguration() {
@@ -172,6 +173,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
172 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 173 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
173 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); 174 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
174 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); 175 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
176 ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
175 177
176 UpdateUIEnabled(); 178 UpdateUIEnabled();
177} 179}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index fac8cf827..75d96d3ab 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2696,6 +2696,22 @@
2696 </widget> 2696 </widget>
2697 </item> 2697 </item>
2698 <item row="5" column="0"> 2698 <item row="5" column="0">
2699 <widget class="QCheckBox" name="enable_joycon_driver">
2700 <property name="toolTip">
2701 <string>Requires restarting yuzu</string>
2702 </property>
2703 <property name="minimumSize">
2704 <size>
2705 <width>0</width>
2706 <height>23</height>
2707 </size>
2708 </property>
2709 <property name="text">
2710 <string>Enable direct JoyCon driver</string>
2711 </property>
2712 </widget>
2713 </item>
2714 <item row="6" column="0">
2699 <widget class="QCheckBox" name="mouse_panning"> 2715 <widget class="QCheckBox" name="mouse_panning">
2700 <property name="minimumSize"> 2716 <property name="minimumSize">
2701 <size> 2717 <size>
@@ -2708,7 +2724,7 @@
2708 </property> 2724 </property>
2709 </widget> 2725 </widget>
2710 </item> 2726 </item>
2711 <item row="5" column="2"> 2727 <item row="6" column="2">
2712 <widget class="QSpinBox" name="mouse_panning_sensitivity"> 2728 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2713 <property name="toolTip"> 2729 <property name="toolTip">
2714 <string>Mouse sensitivity</string> 2730 <string>Mouse sensitivity</string>
@@ -2730,14 +2746,14 @@
2730 </property> 2746 </property>
2731 </widget> 2747 </widget>
2732 </item> 2748 </item>
2733 <item row="6" column="0"> 2749 <item row="7" column="0">
2734 <widget class="QLabel" name="motion_touch"> 2750 <widget class="QLabel" name="motion_touch">
2735 <property name="text"> 2751 <property name="text">
2736 <string>Motion / Touch</string> 2752 <string>Motion / Touch</string>
2737 </property> 2753 </property>
2738 </widget> 2754 </widget>
2739 </item> 2755 </item>
2740 <item row="6" column="2"> 2756 <item row="7" column="2">
2741 <widget class="QPushButton" name="buttonMotionTouch"> 2757 <widget class="QPushButton" name="buttonMotionTouch">
2742 <property name="text"> 2758 <property name="text">
2743 <string>Configure</string> 2759 <string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index c40d980c9..4b7e3b01b 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -66,6 +66,18 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
66 return QObject::tr("R"); 66 return QObject::tr("R");
67 case Common::Input::ButtonNames::TriggerL: 67 case Common::Input::ButtonNames::TriggerL:
68 return QObject::tr("L"); 68 return QObject::tr("L");
69 case Common::Input::ButtonNames::TriggerZR:
70 return QObject::tr("ZR");
71 case Common::Input::ButtonNames::TriggerZL:
72 return QObject::tr("ZL");
73 case Common::Input::ButtonNames::TriggerSR:
74 return QObject::tr("SR");
75 case Common::Input::ButtonNames::TriggerSL:
76 return QObject::tr("SL");
77 case Common::Input::ButtonNames::ButtonStickL:
78 return QObject::tr("Stick L");
79 case Common::Input::ButtonNames::ButtonStickR:
80 return QObject::tr("Stick R");
69 case Common::Input::ButtonNames::ButtonA: 81 case Common::Input::ButtonNames::ButtonA:
70 return QObject::tr("A"); 82 return QObject::tr("A");
71 case Common::Input::ButtonNames::ButtonB: 83 case Common::Input::ButtonNames::ButtonB:
@@ -76,6 +88,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
76 return QObject::tr("Y"); 88 return QObject::tr("Y");
77 case Common::Input::ButtonNames::ButtonStart: 89 case Common::Input::ButtonNames::ButtonStart:
78 return QObject::tr("Start"); 90 return QObject::tr("Start");
91 case Common::Input::ButtonNames::ButtonPlus:
92 return QObject::tr("Plus");
93 case Common::Input::ButtonNames::ButtonMinus:
94 return QObject::tr("Minus");
95 case Common::Input::ButtonNames::ButtonHome:
96 return QObject::tr("Home");
97 case Common::Input::ButtonNames::ButtonCapture:
98 return QObject::tr("Capture");
79 case Common::Input::ButtonNames::L1: 99 case Common::Input::ButtonNames::L1:
80 return QObject::tr("L1"); 100 return QObject::tr("L1");
81 case Common::Input::ButtonNames::L2: 101 case Common::Input::ButtonNames::L2:
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 11390fec0..68af6c20c 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -103,9 +103,13 @@ void PlayerControlPreview::UpdateColors() {
103 103
104 colors.left = colors.primary; 104 colors.left = colors.primary;
105 colors.right = colors.primary; 105 colors.right = colors.primary;
106 // Possible alternative to set colors from settings 106
107 // colors.left = QColor(controller->GetColors().left.body); 107 const auto color_left = controller->GetColorsValues()[0].body;
108 // colors.right = QColor(controller->GetColors().right.body); 108 const auto color_right = controller->GetColorsValues()[1].body;
109 if (color_left != 0 && color_right != 0) {
110 colors.left = QColor(color_left);
111 colors.right = QColor(color_right);
112 }
109} 113}
110 114
111void PlayerControlPreview::ResetInputs() { 115void PlayerControlPreview::ResetInputs() {
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
index d1b870c72..fb1292f07 100644
--- a/src/yuzu/configuration/configure_motion_touch.cpp
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span " 89 "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); 90 "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
91 91
92 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
93 SetConfiguration(); 92 SetConfiguration();
94 UpdateUiDisplay(); 93 UpdateUiDisplay();
95 ConnectEvents(); 94 ConnectEvents();
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 93db47cfd..7e757eafd 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st
66 66
67 setFocusPolicy(Qt::ClickFocus); 67 setFocusPolicy(Qt::ClickFocus);
68 setWindowTitle(tr("Properties")); 68 setWindowTitle(tr("Properties"));
69 // remove Help question mark button from the title bar
70 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
71 69
72 addons_tab->SetTitleId(title_id); 70 addons_tab->SetTitleId(title_id);
73 71
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
index 688c2dd38..1275f10c8 100644
--- a/src/yuzu/configuration/configure_ringcon.cpp
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -4,9 +4,11 @@
4#include <memory> 4#include <memory>
5#include <QKeyEvent> 5#include <QKeyEvent>
6#include <QMenu> 6#include <QMenu>
7#include <QMessageBox>
7#include <QTimer> 8#include <QTimer>
9#include <fmt/format.h>
8 10
9#include "core/hid/emulated_devices.h" 11#include "core/hid/emulated_controller.h"
10#include "core/hid/hid_core.h" 12#include "core/hid/hid_core.h"
11#include "input_common/drivers/keyboard.h" 13#include "input_common/drivers/keyboard.h"
12#include "input_common/drivers/mouse.h" 14#include "input_common/drivers/mouse.h"
@@ -126,9 +128,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
126 ui->buttonRingAnalogPush, 128 ui->buttonRingAnalogPush,
127 }; 129 };
128 130
129 emulated_device = hid_core_.GetEmulatedDevices(); 131 emulated_controller = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
130 emulated_device->SaveCurrentConfig(); 132 emulated_controller->SaveCurrentConfig();
131 emulated_device->EnableConfiguration(); 133 emulated_controller->EnableConfiguration();
134
135 Core::HID::ControllerUpdateCallback engine_callback{
136 .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
137 .is_npad_service = false,
138 };
139 callback_key = emulated_controller->SetCallback(engine_callback);
140 is_controller_set = true;
132 141
133 LoadConfiguration(); 142 LoadConfiguration();
134 143
@@ -143,9 +152,9 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
143 HandleClick( 152 HandleClick(
144 analog_map_buttons[sub_button_id], 153 analog_map_buttons[sub_button_id],
145 [=, this](const Common::ParamPackage& params) { 154 [=, this](const Common::ParamPackage& params) {
146 Common::ParamPackage param = emulated_device->GetRingParam(); 155 Common::ParamPackage param = emulated_controller->GetRingParam();
147 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]); 156 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
148 emulated_device->SetRingParam(param); 157 emulated_controller->SetRingParam(param);
149 }, 158 },
150 InputCommon::Polling::InputType::Stick); 159 InputCommon::Polling::InputType::Stick);
151 }); 160 });
@@ -155,16 +164,16 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
155 connect(analog_button, &QPushButton::customContextMenuRequested, 164 connect(analog_button, &QPushButton::customContextMenuRequested,
156 [=, this](const QPoint& menu_location) { 165 [=, this](const QPoint& menu_location) {
157 QMenu context_menu; 166 QMenu context_menu;
158 Common::ParamPackage param = emulated_device->GetRingParam(); 167 Common::ParamPackage param = emulated_controller->GetRingParam();
159 context_menu.addAction(tr("Clear"), [&] { 168 context_menu.addAction(tr("Clear"), [&] {
160 emulated_device->SetRingParam({}); 169 emulated_controller->SetRingParam(param);
161 analog_map_buttons[sub_button_id]->setText(tr("[not set]")); 170 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
162 }); 171 });
163 context_menu.addAction(tr("Invert axis"), [&] { 172 context_menu.addAction(tr("Invert axis"), [&] {
164 const bool invert_value = param.Get("invert_x", "+") == "-"; 173 const bool invert_value = param.Get("invert_x", "+") == "-";
165 const std::string invert_str = invert_value ? "+" : "-"; 174 const std::string invert_str = invert_value ? "+" : "-";
166 param.Set("invert_x", invert_str); 175 param.Set("invert_x", invert_str);
167 emulated_device->SetRingParam(param); 176 emulated_controller->SetRingParam(param);
168 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM; 177 for (int sub_button_id2 = 0; sub_button_id2 < ANALOG_SUB_BUTTONS_NUM;
169 ++sub_button_id2) { 178 ++sub_button_id2) {
170 analog_map_buttons[sub_button_id2]->setText( 179 analog_map_buttons[sub_button_id2]->setText(
@@ -177,16 +186,19 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
177 } 186 }
178 187
179 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] { 188 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
180 Common::ParamPackage param = emulated_device->GetRingParam(); 189 Common::ParamPackage param = emulated_controller->GetRingParam();
181 const auto slider_value = ui->sliderRingAnalogDeadzone->value(); 190 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
182 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value)); 191 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
183 param.Set("deadzone", slider_value / 100.0f); 192 param.Set("deadzone", slider_value / 100.0f);
184 emulated_device->SetRingParam(param); 193 emulated_controller->SetRingParam(param);
185 }); 194 });
186 195
187 connect(ui->restore_defaults_button, &QPushButton::clicked, this, 196 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
188 &ConfigureRingController::RestoreDefaults); 197 &ConfigureRingController::RestoreDefaults);
189 198
199 connect(ui->enable_ring_controller_button, &QPushButton::clicked, this,
200 &ConfigureRingController::EnableRingController);
201
190 timeout_timer->setSingleShot(true); 202 timeout_timer->setSingleShot(true);
191 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); }); 203 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
192 204
@@ -202,7 +214,14 @@ ConfigureRingController::ConfigureRingController(QWidget* parent,
202} 214}
203 215
204ConfigureRingController::~ConfigureRingController() { 216ConfigureRingController::~ConfigureRingController() {
205 emulated_device->DisableConfiguration(); 217 emulated_controller->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
218 Common::Input::PollingMode::Active);
219 emulated_controller->DisableConfiguration();
220
221 if (is_controller_set) {
222 emulated_controller->DeleteCallback(callback_key);
223 is_controller_set = false;
224 }
206}; 225};
207 226
208void ConfigureRingController::changeEvent(QEvent* event) { 227void ConfigureRingController::changeEvent(QEvent* event) {
@@ -219,7 +238,7 @@ void ConfigureRingController::RetranslateUI() {
219 238
220void ConfigureRingController::UpdateUI() { 239void ConfigureRingController::UpdateUI() {
221 RetranslateUI(); 240 RetranslateUI();
222 const Common::ParamPackage param = emulated_device->GetRingParam(); 241 const Common::ParamPackage param = emulated_controller->GetRingParam();
223 242
224 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 243 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
225 auto* const analog_button = analog_map_buttons[sub_button_id]; 244 auto* const analog_button = analog_map_buttons[sub_button_id];
@@ -240,9 +259,9 @@ void ConfigureRingController::UpdateUI() {
240} 259}
241 260
242void ConfigureRingController::ApplyConfiguration() { 261void ConfigureRingController::ApplyConfiguration() {
243 emulated_device->DisableConfiguration(); 262 emulated_controller->DisableConfiguration();
244 emulated_device->SaveCurrentConfig(); 263 emulated_controller->SaveCurrentConfig();
245 emulated_device->EnableConfiguration(); 264 emulated_controller->EnableConfiguration();
246} 265}
247 266
248void ConfigureRingController::LoadConfiguration() { 267void ConfigureRingController::LoadConfiguration() {
@@ -252,10 +271,62 @@ void ConfigureRingController::LoadConfiguration() {
252void ConfigureRingController::RestoreDefaults() { 271void ConfigureRingController::RestoreDefaults() {
253 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( 272 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
254 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); 273 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
255 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string)); 274 emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string));
256 UpdateUI(); 275 UpdateUI();
257} 276}
258 277
278void ConfigureRingController::EnableRingController() {
279 const auto dialog_title = tr("Error enabling ring input");
280
281 is_ring_enabled = false;
282 ui->ring_controller_sensor_value->setText(tr("Not connected"));
283
284 if (!Settings::values.enable_joycon_driver) {
285 QMessageBox::warning(this, dialog_title, tr("Direct Joycon driver is not enabled"));
286 return;
287 }
288
289 ui->enable_ring_controller_button->setEnabled(false);
290 ui->enable_ring_controller_button->setText(tr("Configuring"));
291 // SetPollingMode is blocking. Allow to update the button status before calling the command
292 repaint();
293
294 const auto result = emulated_controller->SetPollingMode(
295 Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::Ring);
296 switch (result) {
297 case Common::Input::DriverResult::Success:
298 is_ring_enabled = true;
299 break;
300 case Common::Input::DriverResult::NotSupported:
301 QMessageBox::warning(this, dialog_title,
302 tr("The current mapped device doesn't support the ring controller"));
303 break;
304 case Common::Input::DriverResult::NoDeviceDetected:
305 QMessageBox::warning(this, dialog_title,
306 tr("The current mapped device doesn't have a ring attached"));
307 break;
308 default:
309 QMessageBox::warning(this, dialog_title,
310 tr("Unexpected driver result %1").arg(static_cast<int>(result)));
311 break;
312 }
313 ui->enable_ring_controller_button->setEnabled(true);
314 ui->enable_ring_controller_button->setText(tr("Enable"));
315}
316
317void ConfigureRingController::ControllerUpdate(Core::HID::ControllerTriggerType type) {
318 if (!is_ring_enabled) {
319 return;
320 }
321 if (type != Core::HID::ControllerTriggerType::RingController) {
322 return;
323 }
324
325 const auto value = emulated_controller->GetRingSensorValues();
326 const auto tex_value = QString::fromStdString(fmt::format("{:.3f}", value.raw_value));
327 ui->ring_controller_sensor_value->setText(tex_value);
328}
329
259void ConfigureRingController::HandleClick( 330void ConfigureRingController::HandleClick(
260 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 331 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
261 InputCommon::Polling::InputType type) { 332 InputCommon::Polling::InputType type) {
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
index 38a9cb716..b23c27906 100644
--- a/src/yuzu/configuration/configure_ringcon.h
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -13,7 +13,7 @@ class InputSubsystem;
13 13
14namespace Core::HID { 14namespace Core::HID {
15class HIDCore; 15class HIDCore;
16class EmulatedDevices; 16class EmulatedController;
17} // namespace Core::HID 17} // namespace Core::HID
18 18
19namespace Ui { 19namespace Ui {
@@ -42,6 +42,12 @@ private:
42 /// Restore all buttons to their default values. 42 /// Restore all buttons to their default values.
43 void RestoreDefaults(); 43 void RestoreDefaults();
44 44
45 /// Sets current polling mode to ring input
46 void EnableRingController();
47
48 // Handles emulated controller events
49 void ControllerUpdate(Core::HID::ControllerTriggerType type);
50
45 /// Called when the button was pressed. 51 /// Called when the button was pressed.
46 void HandleClick(QPushButton* button, 52 void HandleClick(QPushButton* button,
47 std::function<void(const Common::ParamPackage&)> new_input_setter, 53 std::function<void(const Common::ParamPackage&)> new_input_setter,
@@ -78,7 +84,11 @@ private:
78 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter; 84 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
79 85
80 InputCommon::InputSubsystem* input_subsystem; 86 InputCommon::InputSubsystem* input_subsystem;
81 Core::HID::EmulatedDevices* emulated_device; 87 Core::HID::EmulatedController* emulated_controller;
88
89 bool is_ring_enabled{};
90 bool is_controller_set{};
91 int callback_key;
82 92
83 std::unique_ptr<Ui::ConfigureRingController> ui; 93 std::unique_ptr<Ui::ConfigureRingController> ui;
84}; 94};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
index 9ec634dd4..514dff372 100644
--- a/src/yuzu/configuration/configure_ringcon.ui
+++ b/src/yuzu/configuration/configure_ringcon.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>298</width> 9 <width>315</width>
10 <height>339</height> 10 <height>400</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -46,187 +46,283 @@
46 </property> 46 </property>
47 </spacer> 47 </spacer>
48 </item> 48 </item>
49 <item> 49 <item>
50 <widget class="QGroupBox" name="RingAnalog"> 50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title"> 51 <property name="title">
52 <string>Ring Sensor Parameters</string> 52 <string>Virtual Ring Sensor Parameters</string>
53 </property> 53 </property>
54 <property name="alignment"> 54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> 55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property> 56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3"> 57 <layout class="QVBoxLayout" name="verticalLayout_1">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
63 </property>
64 <property name="leftMargin">
65 <number>3</number>
66 </property>
67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing"> 58 <property name="spacing">
79 <number>3</number> 59 <number>0</number>
80 </property> 60 </property>
81 <item alignment="Qt::AlignHCenter"> 61 <property name="sizeConstraint">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup"> 62 <enum>QLayout::SetDefaultConstraint</enum>
83 <property name="title"> 63 </property>
84 <string>Pull</string> 64 <property name="leftMargin">
85 </property> 65 <number>3</number>
86 <property name="alignment"> 66 </property>
87 <set>Qt::AlignCenter</set> 67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing">
79 <number>3</number>
88 </property> 80 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout"> 81 <item alignment="Qt::AlignHCenter">
90 <property name="spacing"> 82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
91 <number>3</number> 83 <property name="title">
84 <string>Pull</string>
92 </property> 85 </property>
93 <property name="leftMargin"> 86 <property name="alignment">
94 <number>3</number> 87 <set>Qt::AlignCenter</set>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property> 88 </property>
99 <property name="rightMargin"> 89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
100 <number>3</number> 90 <property name="spacing">
91 <number>3</number>
92 </property>
93 <property name="leftMargin">
94 <number>3</number>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property>
99 <property name="rightMargin">
100 <number>3</number>
101 </property>
102 <property name="bottomMargin">
103 <number>3</number>
104 </property>
105 <item>
106 <widget class="QPushButton" name="buttonRingAnalogPull">
107 <property name="minimumSize">
108 <size>
109 <width>70</width>
110 <height>0</height>
111 </size>
112 </property>
113 <property name="maximumSize">
114 <size>
115 <width>68</width>
116 <height>16777215</height>
117 </size>
118 </property>
119 <property name="styleSheet">
120 <string notr="true">min-width: 68px;</string>
121 </property>
122 <property name="text">
123 <string>Pull</string>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </widget>
129 </item>
130 <item alignment="Qt::AlignHCenter">
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
132 <property name="title">
133 <string>Push</string>
101 </property> 134 </property>
102 <property name="bottomMargin"> 135 <property name="alignment">
103 <number>3</number> 136 <set>Qt::AlignCenter</set>
104 </property> 137 </property>
105 <item> 138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
106 <widget class="QPushButton" name="buttonRingAnalogPull"> 139 <property name="spacing">
107 <property name="minimumSize"> 140 <number>3</number>
108 <size> 141 </property>
109 <width>68</width> 142 <property name="leftMargin">
110 <height>0</height> 143 <number>3</number>
111 </size> 144 </property>
112 </property> 145 <property name="topMargin">
113 <property name="maximumSize"> 146 <number>3</number>
114 <size> 147 </property>
115 <width>68</width> 148 <property name="rightMargin">
116 <height>16777215</height> 149 <number>3</number>
117 </size> 150 </property>
118 </property> 151 <property name="bottomMargin">
119 <property name="styleSheet"> 152 <number>3</number>
120 <string notr="true">min-width: 68px;</string> 153 </property>
121 </property> 154 <item>
122 <property name="text"> 155 <widget class="QPushButton" name="buttonRingAnalogPush">
123 <string>Pull</string> 156 <property name="minimumSize">
124 </property> 157 <size>
125 </widget> 158 <width>70</width>
126 </item> 159 <height>0</height>
127 </layout> 160 </size>
128 </widget> 161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text">
172 <string>Push</string>
173 </property>
174 </widget>
175 </item>
176 </layout>
177 </widget>
178 </item>
179 </layout>
129 </item> 180 </item>
130 <item alignment="Qt::AlignHCenter"> 181 <item>
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup"> 182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
132 <property name="title"> 183 <property name="spacing">
133 <string>Push</string> 184 <number>3</number>
134 </property> 185 </property>
135 <property name="alignment"> 186 <property name="sizeConstraint">
136 <set>Qt::AlignCenter</set> 187 <enum>QLayout::SetDefaultConstraint</enum>
137 </property> 188 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout"> 189 <property name="leftMargin">
139 <property name="spacing"> 190 <number>0</number>
140 <number>3</number> 191 </property>
141 </property> 192 <property name="topMargin">
142 <property name="leftMargin"> 193 <number>10</number>
143 <number>3</number> 194 </property>
144 </property> 195 <property name="rightMargin">
145 <property name="topMargin"> 196 <number>0</number>
146 <number>3</number> 197 </property>
147 </property> 198 <property name="bottomMargin">
148 <property name="rightMargin"> 199 <number>3</number>
149 <number>3</number> 200 </property>
150 </property> 201 <item>
151 <property name="bottomMargin"> 202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
152 <number>3</number>
153 </property>
154 <item> 203 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush"> 204 <widget class="QLabel" name="labelRingAnalogDeadzone">
156 <property name="minimumSize">
157 <size>
158 <width>68</width>
159 <height>0</height>
160 </size>
161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text"> 205 <property name="text">
172 <string>Push</string> 206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
173 </property> 210 </property>
174 </widget> 211 </widget>
175 </item> 212 </item>
176 </layout> 213 </layout>
177 </widget> 214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item>
225 </layout>
178 </item> 226 </item>
179 </layout> 227 </layout>
180 </item> 228 </widget>
181 <item> 229 </item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout"> 230 <item>
231 <widget class="QGroupBox" name="RingDriver">
232 <property name="title">
233 <string>Direct Joycon Driver</string>
234 </property>
235 <property name="alignment">
236 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
237 </property>
238 <layout class="QVBoxLayout" name="verticalLayout_2">
183 <property name="spacing"> 239 <property name="spacing">
184 <number>3</number> 240 <number>0</number>
185 </property> 241 </property>
186 <property name="sizeConstraint"> 242 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum> 243 <enum>QLayout::SetDefaultConstraint</enum>
188 </property> 244 </property>
189 <property name="leftMargin"> 245 <property name="leftMargin">
190 <number>0</number> 246 <number>3</number>
191 </property> 247 </property>
192 <property name="topMargin"> 248 <property name="topMargin">
193 <number>10</number> 249 <number>6</number>
194 </property> 250 </property>
195 <property name="rightMargin"> 251 <property name="rightMargin">
196 <number>0</number> 252 <number>3</number>
197 </property> 253 </property>
198 <property name="bottomMargin"> 254 <property name="bottomMargin">
199 <number>3</number> 255 <number>10</number>
200 </property> 256 </property>
201 <item> 257 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout"> 258 <layout class="QGridLayout" name="gridLayout">
203 <item> 259 <property name="leftMargin">
204 <widget class="QLabel" name="labelRingAnalogDeadzone"> 260 <number>10</number>
261 </property>
262 <property name="topMargin">
263 <number>6</number>
264 </property>
265 <property name="rightMargin">
266 <number>10</number>
267 </property>
268 <property name="bottomMargin">
269 <number>10</number>
270 </property>
271 <property name="verticalSpacing">
272 <number>10</number>
273 </property>
274 <item row="0" column="1">
275 <spacer name="horizontalSpacer">
276 <property name="orientation">
277 <enum>Qt::Horizontal</enum>
278 </property>
279 <property name="sizeType">
280 <enum>QSizePolicy::Fixed</enum>
281 </property>
282 <property name="sizeHint" stdset="0">
283 <size>
284 <width>76</width>
285 <height>20</height>
286 </size>
287 </property>
288 </spacer>
289 </item>
290 <item row="0" column="0">
291 <widget class="QLabel" name="enable_ring_controller_label">
292 <property name="text">
293 <string>Enable Ring Input</string>
294 </property>
295 </widget>
296 </item>
297 <item row="0" column="2">
298 <widget class="QPushButton" name="enable_ring_controller_button">
205 <property name="text"> 299 <property name="text">
206 <string>Deadzone: 0%</string> 300 <string>Enable</string>
301 </property>
302 </widget>
303 </item>
304 <item row="1" column="0">
305 <widget class="QLabel" name="ring_controller_sensor_label">
306 <property name="text">
307 <string>Ring Sensor Value</string>
308 </property>
309 </widget>
310 </item>
311 <item row="1" column="2">
312 <widget class="QLabel" name="ring_controller_sensor_value">
313 <property name="text">
314 <string>Not connected</string>
207 </property> 315 </property>
208 <property name="alignment"> 316 <property name="alignment">
209 <set>Qt::AlignHCenter</set> 317 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
210 </property> 318 </property>
211 </widget> 319 </widget>
212 </item> 320 </item>
213 </layout> 321 </layout>
214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item> 322 </item>
225 </layout> 323 </layout>
226 </item> 324 </widget>
227 </layout> 325 </item>
228 </widget>
229 </item>
230 <item> 326 <item>
231 <spacer name="verticalSpacer"> 327 <spacer name="verticalSpacer">
232 <property name="orientation"> 328 <property name="orientation">
@@ -273,6 +369,6 @@
273 <signal>rejected()</signal> 369 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver> 370 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot> 371 <slot>reject()</slot>
276 </connection> 372 </connection>
277 </connections> 373 </connections>
278</ui> 374</ui>
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 94049f2f4..9ea4c02da 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -31,6 +31,9 @@ constexpr std::array<u32, 7> LOCALE_BLOCKLIST{
31}; 31};
32 32
33static bool IsValidLocale(u32 region_index, u32 language_index) { 33static bool IsValidLocale(u32 region_index, u32 language_index) {
34 if (region_index >= LOCALE_BLOCKLIST.size()) {
35 return false;
36 }
34 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0; 37 return ((LOCALE_BLOCKLIST.at(region_index) >> language_index) & 1) == 0;
35} 38}
36 39
@@ -55,8 +58,11 @@ ConfigureSystem::ConfigureSystem(Core::System& system_, QWidget* parent)
55 }); 58 });
56 59
57 const auto locale_check = [this](int index) { 60 const auto locale_check = [this](int index) {
58 const bool valid_locale = 61 const auto region_index = ConfigurationShared::GetComboboxIndex(
59 IsValidLocale(ui->combo_region->currentIndex(), ui->combo_language->currentIndex()); 62 Settings::values.region_index.GetValue(true), ui->combo_region);
63 const auto language_index = ConfigurationShared::GetComboboxIndex(
64 Settings::values.language_index.GetValue(true), ui->combo_language);
65 const bool valid_locale = IsValidLocale(region_index, language_index);
60 ui->label_warn_invalid_locale->setVisible(!valid_locale); 66 ui->label_warn_invalid_locale->setVisible(!valid_locale);
61 if (!valid_locale) { 67 if (!valid_locale) {
62 ui->label_warn_invalid_locale->setText( 68 ui->label_warn_invalid_locale->setText(
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h
index 8f02880a7..a7f086258 100644
--- a/src/yuzu/configuration/configure_system.h
+++ b/src/yuzu/configuration/configure_system.h
@@ -42,13 +42,7 @@ private:
42 std::unique_ptr<Ui::ConfigureSystem> ui; 42 std::unique_ptr<Ui::ConfigureSystem> ui;
43 bool enabled = false; 43 bool enabled = false;
44 44
45 int language_index = 0;
46 int region_index = 0;
47 int time_zone_index = 0;
48 int sound_index = 0;
49
50 ConfigurationShared::CheckState use_rng_seed; 45 ConfigurationShared::CheckState use_rng_seed;
51 ConfigurationShared::CheckState use_custom_rtc;
52 46
53 Core::System& system; 47 Core::System& system;
54}; 48};
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 1edc5f1f3..5a545aa70 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/src/yuzu/configuration/configure_tas.cpp
@@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)
17 17
18 setFocusPolicy(Qt::ClickFocus); 18 setFocusPolicy(Qt::ClickFocus);
19 setWindowTitle(tr("TAS Configuration")); 19 setWindowTitle(tr("TAS Configuration"));
20 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
21 20
22 connect(ui->tas_path_button, &QToolButton::pressed, this, 21 connect(ui->tas_path_button, &QToolButton::pressed, this,
23 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); 22 [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); });
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 19f3775a3..e2f55ebae 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,
20 setWindowTitle(tr("Controller P1")); 20 setWindowTitle(tr("Controller P1"));
21 resize(500, 350); 21 resize(500, 350);
22 setMinimumSize(500, 350); 22 setMinimumSize(500, 350);
23 // Remove the "?" button from the titlebar and enable the maximize button 23 // Enable the maximize button
24 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 24 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
25 Qt::WindowMaximizeButtonHint);
26 25
27 widget = new PlayerControlPreview(this); 26 widget = new PlayerControlPreview(this);
28 refreshConfiguration(); 27 refreshConfiguration();
diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp
index d3e2d3c12..493ee0b17 100644
--- a/src/yuzu/debugger/profiler.cpp
+++ b/src/yuzu/debugger/profiler.cpp
@@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di
49 setObjectName(QStringLiteral("MicroProfile")); 49 setObjectName(QStringLiteral("MicroProfile"));
50 setWindowTitle(tr("&MicroProfile")); 50 setWindowTitle(tr("&MicroProfile"));
51 resize(1000, 600); 51 resize(1000, 600);
52 // Remove the "?" button from the titlebar and enable the maximize button 52 // Enable the maximize button
53 setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | 53 setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
54 Qt::WindowMaximizeButtonHint);
55 54
56#if MICROPROFILE_ENABLED 55#if MICROPROFILE_ENABLED
57 56
diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp
index 13723f6e5..6530186c1 100644
--- a/src/yuzu/hotkeys.cpp
+++ b/src/yuzu/hotkeys.cpp
@@ -21,7 +21,7 @@ void HotkeyRegistry::SaveHotkeys() {
21 {hotkey.first, group.first, 21 {hotkey.first, group.first,
22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), 22 UISettings::ContextualShortcut({hotkey.second.keyseq.toString(),
23 hotkey.second.controller_keyseq, 23 hotkey.second.controller_keyseq,
24 hotkey.second.context})}); 24 hotkey.second.context, hotkey.second.repeat})});
25 } 25 }
26 } 26 }
27} 27}
@@ -47,6 +47,7 @@ void HotkeyRegistry::LoadHotkeys() {
47 hk.controller_shortcut->disconnect(); 47 hk.controller_shortcut->disconnect();
48 hk.controller_shortcut->SetKey(hk.controller_keyseq); 48 hk.controller_shortcut->SetKey(hk.controller_keyseq);
49 } 49 }
50 hk.repeat = shortcut.shortcut.repeat;
50 } 51 }
51} 52}
52 53
@@ -57,8 +58,7 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action
57 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context); 58 hk.shortcut = new QShortcut(hk.keyseq, widget, nullptr, nullptr, hk.context);
58 } 59 }
59 60
60 hk.shortcut->setAutoRepeat(false); 61 hk.shortcut->setAutoRepeat(hk.repeat);
61
62 return hk.shortcut; 62 return hk.shortcut;
63} 63}
64 64
diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h
index dc5b7f628..848239c35 100644
--- a/src/yuzu/hotkeys.h
+++ b/src/yuzu/hotkeys.h
@@ -115,6 +115,7 @@ private:
115 QShortcut* shortcut = nullptr; 115 QShortcut* shortcut = nullptr;
116 ControllerShortcut* controller_shortcut = nullptr; 116 ControllerShortcut* controller_shortcut = nullptr;
117 Qt::ShortcutContext context = Qt::WindowShortcut; 117 Qt::ShortcutContext context = Qt::WindowShortcut;
118 bool repeat;
118 }; 119 };
119 120
120 using HotkeyMap = std::map<QString, Hotkey>; 121 using HotkeyMap = std::map<QString, Hotkey>;
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp
index 84ec4fe13..673bbaa83 100644
--- a/src/yuzu/install_dialog.cpp
+++ b/src/yuzu/install_dialog.cpp
@@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo
46 vbox_layout->addLayout(hbox_layout); 46 vbox_layout->addLayout(hbox_layout);
47 47
48 setLayout(vbox_layout); 48 setLayout(vbox_layout);
49 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
50 setWindowTitle(tr("Install Files to NAND")); 49 setWindowTitle(tr("Install Files to NAND"));
51} 50}
52 51
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 06ae12202..42b7b64c8 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2753,8 +2753,7 @@ void GMainWindow::OnMenuInstallToNAND() {
2753 ui->action_Install_File_NAND->setEnabled(false); 2753 ui->action_Install_File_NAND->setEnabled(false);
2754 2754
2755 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); 2755 install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this);
2756 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & 2756 install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
2757 ~Qt::WindowMaximizeButtonHint);
2758 install_progress->setAttribute(Qt::WA_DeleteOnClose, true); 2757 install_progress->setAttribute(Qt::WA_DeleteOnClose, true);
2759 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); 2758 install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);
2760 install_progress->show(); 2759 install_progress->show();
@@ -4447,6 +4446,11 @@ int main(int argc, char* argv[]) {
4447 } 4446 }
4448#endif 4447#endif
4449 4448
4449#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4450 // Disables the "?" button on all dialogs. Disabled by default on Qt6.
4451 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
4452#endif
4453
4450 // Enables the core to make the qt created contexts current on std::threads 4454 // Enables the core to make the qt created contexts current on std::threads
4451 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4455 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4452 QApplication app(argc, argv); 4456 QApplication app(argc, argv);
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2006b883e..db43b7033 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -22,6 +22,7 @@ struct ContextualShortcut {
22 QString keyseq; 22 QString keyseq;
23 QString controller_keyseq; 23 QString controller_keyseq;
24 int context; 24 int context;
25 bool repeat;
25}; 26};
26 27
27struct Shortcut { 28struct Shortcut {
diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp
index bbb370595..5f6a9c193 100644
--- a/src/yuzu/util/limitable_input_dialog.cpp
+++ b/src/yuzu/util/limitable_input_dialog.cpp
@@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {
16LimitableInputDialog::~LimitableInputDialog() = default; 16LimitableInputDialog::~LimitableInputDialog() = default;
17 17
18void LimitableInputDialog::CreateUI() { 18void LimitableInputDialog::CreateUI() {
19 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
20
21 text_label = new QLabel(this); 19 text_label = new QLabel(this);
22 text_entry = new QLineEdit(this); 20 text_entry = new QLineEdit(this);
23 text_label_invalid = new QLabel(this); 21 text_label_invalid = new QLabel(this);
diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
index 4b10fa517..1670aa596 100644
--- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
+++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp
@@ -8,7 +8,6 @@
8 8
9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) { 9SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {
10 setWindowTitle(tr("Enter a hotkey")); 10 setWindowTitle(tr("Enter a hotkey"));
11 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
12 11
13 key_sequence = new QKeySequenceEdit; 12 key_sequence = new QKeySequenceEdit;
14 13
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6fcf04e1b..67d230462 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -5,8 +5,8 @@
5 5
6namespace DefaultINI { 6namespace DefaultINI {
7 7
8const char* sdl2_config_file = R"( 8const char* sdl2_config_file =
9 9 R"(
10[ControlsP0] 10[ControlsP0]
11# The input devices and parameters for each Switch native input 11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... 12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
@@ -143,6 +143,8 @@ mouse_enabled =
143# 0 (default): Disabled, 1: Enabled 143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled = 144keyboard_enabled =
145 145
146)"
147 R"(
146[Core] 148[Core]
147# Whether to use multi-core for CPU emulation 149# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled 150# 0: Disabled, 1 (default): Enabled
@@ -242,6 +244,8 @@ cpuopt_unsafe_fastmem_check =
242# 0: Disabled, 1 (default): Enabled 244# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor = 245cpuopt_unsafe_ignore_global_monitor =
244 246
247)"
248 R"(
245[Renderer] 249[Renderer]
246# Which backend API to use. 250# Which backend API to use.
247# 0: OpenGL, 1 (default): Vulkan 251# 0: OpenGL, 1 (default): Vulkan
@@ -360,6 +364,8 @@ bg_red =
360bg_blue = 364bg_blue =
361bg_green = 365bg_green =
362 366
367)"
368 R"(
363[Audio] 369[Audio]
364# Which audio output engine to use. 370# Which audio output engine to use.
365# auto (default): Auto-select 371# auto (default): Auto-select
diff --git a/vcpkg.json b/vcpkg.json
index 3c92510d6..ef271f778 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -40,7 +40,7 @@
40 "overrides": [ 40 "overrides": [
41 { 41 {
42 "name": "catch2", 42 "name": "catch2",
43 "version": "2.13.9" 43 "version": "3.0.1"
44 }, 44 },
45 { 45 {
46 "name": "fmt", 46 "name": "fmt",