summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/renderer/performance/performance_manager.cpp1
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/atomic_helpers.h1
-rw-r--r--src/common/cache_management.cpp60
-rw-r--r--src/common/cache_management.h27
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp1
-rw-r--r--src/core/debugger/debugger.cpp161
-rw-r--r--src/core/debugger/gdbstub.cpp151
-rw-r--r--src/core/debugger/gdbstub.h1
-rw-r--r--src/core/frontend/applets/cabinet.cpp20
-rw-r--r--src/core/frontend/applets/cabinet.h37
-rw-r--r--src/core/hid/emulated_console.cpp94
-rw-r--r--src/core/hid/emulated_console.h15
-rw-r--r--src/core/hid/input_converter.cpp3
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp1
-rw-r--r--src/core/hle/kernel/k_page_table.cpp3
-rw-r--r--src/core/hle/kernel/k_page_table.h3
-rw-r--r--src/core/hle/kernel/k_process.cpp1
-rw-r--r--src/core/hle/kernel/svc.cpp26
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
-rw-r--r--src/core/hle/result.h76
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.cpp177
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.h104
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp1
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp2
-rw-r--r--src/core/hle/service/am/applets/applets.cpp20
-rw-r--r--src/core/hle/service/am/applets/applets.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp32
-rw-r--r--src/core/hle/service/hid/hid.h4
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp35
-rw-r--r--src/core/hle/service/nfp/nfp_device.h6
-rw-r--r--src/core/hle/service/nfp/nfp_types.h17
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp5
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h5
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp14
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/service.cpp1
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp1
-rw-r--r--src/core/memory.cpp65
-rw-r--r--src/core/memory.h34
-rw-r--r--src/input_common/drivers/virtual_amiibo.cpp15
-rw-r--r--src/input_common/drivers/virtual_amiibo.h3
-rw-r--r--src/input_common/helpers/touch_from_buttons.cpp9
-rw-r--r--src/input_common/input_engine.h2
-rw-r--r--src/input_common/input_poller.cpp18
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp3
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.cpp6
-rw-r--r--src/video_core/engines/maxwell_3d.h6
-rw-r--r--src/video_core/engines/maxwell_dma.cpp1
-rw-r--r--src/video_core/engines/puller.cpp4
-rw-r--r--src/video_core/macro/macro_hle.cpp16
-rw-r--r--src/video_core/macro/macro_interpreter.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp1
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h2
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp1
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp1
-rw-r--r--src/video_core/textures/decoders.cpp3
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.cpp260
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.h83
-rw-r--r--src/yuzu/applets/qt_amiibo_settings.ui494
-rw-r--r--src/yuzu/compatdb.cpp1
-rw-r--r--src/yuzu/configuration/configure_profile_manager.cpp67
-rw-r--r--src/yuzu/configuration/configure_profile_manager.h27
-rw-r--r--src/yuzu/configuration/configure_profile_manager.ui6
-rw-r--r--src/yuzu/main.cpp29
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp1
84 files changed, 2159 insertions, 190 deletions
diff --git a/src/audio_core/renderer/performance/performance_manager.cpp b/src/audio_core/renderer/performance/performance_manager.cpp
index fd5873e1e..8aa0f5ed0 100644
--- a/src/audio_core/renderer/performance/performance_manager.cpp
+++ b/src/audio_core/renderer/performance/performance_manager.cpp
@@ -26,6 +26,7 @@ void PerformanceManager::CreateImpl(const size_t version) {
26 impl = std::make_unique< 26 impl = std::make_unique<
27 PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1, 27 PerformanceManagerImpl<PerformanceVersion::Version1, PerformanceFrameHeaderVersion1,
28 PerformanceEntryVersion1, PerformanceDetailVersion1>>(); 28 PerformanceEntryVersion1, PerformanceDetailVersion1>>();
29 break;
29 } 30 }
30} 31}
31 32
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c0555f840..b7c15c191 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -34,6 +34,8 @@ add_library(common STATIC
34 bit_util.h 34 bit_util.h
35 cityhash.cpp 35 cityhash.cpp
36 cityhash.h 36 cityhash.h
37 cache_management.cpp
38 cache_management.h
37 common_funcs.h 39 common_funcs.h
38 common_types.h 40 common_types.h
39 concepts.h 41 concepts.h
diff --git a/src/common/atomic_helpers.h b/src/common/atomic_helpers.h
index bef5015c1..aef3b66a4 100644
--- a/src/common/atomic_helpers.h
+++ b/src/common/atomic_helpers.h
@@ -156,6 +156,7 @@ AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN {
156 break; 156 break;
157 default: 157 default:
158 assert(false); 158 assert(false);
159 break;
159 } 160 }
160} 161}
161 162
diff --git a/src/common/cache_management.cpp b/src/common/cache_management.cpp
new file mode 100644
index 000000000..57810b76a
--- /dev/null
+++ b/src/common/cache_management.cpp
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cstring>
5
6#include "alignment.h"
7#include "cache_management.h"
8#include "common_types.h"
9
10namespace Common {
11
12#if defined(ARCHITECTURE_x86_64)
13
14// Most cache operations are no-ops on x86
15
16void DataCacheLineCleanByVAToPoU(void* start, size_t size) {}
17void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size) {}
18void DataCacheLineCleanByVAToPoC(void* start, size_t size) {}
19void DataCacheZeroByVA(void* start, size_t size) {
20 std::memset(start, 0, size);
21}
22
23#elif defined(ARCHITECTURE_arm64)
24
25// BS/DminLine is log2(cache size in words), we want size in bytes
26#define EXTRACT_DMINLINE(ctr_el0) (1 << ((((ctr_el0) >> 16) & 0xf) + 2))
27#define EXTRACT_BS(dczid_el0) (1 << (((dczid_el0)&0xf) + 2))
28
29#define DEFINE_DC_OP(op_name, function_name) \
30 void function_name(void* start, size_t size) { \
31 size_t ctr_el0; \
32 asm volatile("mrs %[ctr_el0], ctr_el0\n\t" : [ctr_el0] "=r"(ctr_el0)); \
33 size_t cacheline_size = EXTRACT_DMINLINE(ctr_el0); \
34 uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
35 uintptr_t va_end = va_start + size; \
36 for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
37 asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
38 } \
39 }
40
41#define DEFINE_DC_OP_DCZID(op_name, function_name) \
42 void function_name(void* start, size_t size) { \
43 size_t dczid_el0; \
44 asm volatile("mrs %[dczid_el0], dczid_el0\n\t" : [dczid_el0] "=r"(dczid_el0)); \
45 size_t cacheline_size = EXTRACT_BS(dczid_el0); \
46 uintptr_t va_start = reinterpret_cast<uintptr_t>(start); \
47 uintptr_t va_end = va_start + size; \
48 for (uintptr_t va = va_start; va < va_end; va += cacheline_size) { \
49 asm volatile("dc " #op_name ", %[va]\n\t" : : [va] "r"(va) : "memory"); \
50 } \
51 }
52
53DEFINE_DC_OP(cvau, DataCacheLineCleanByVAToPoU);
54DEFINE_DC_OP(civac, DataCacheLineCleanAndInvalidateByVAToPoC);
55DEFINE_DC_OP(cvac, DataCacheLineCleanByVAToPoC);
56DEFINE_DC_OP_DCZID(zva, DataCacheZeroByVA);
57
58#endif
59
60} // namespace Common
diff --git a/src/common/cache_management.h b/src/common/cache_management.h
new file mode 100644
index 000000000..e467b87e4
--- /dev/null
+++ b/src/common/cache_management.h
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "stdlib.h"
7
8namespace Common {
9
10// Data cache instructions enabled at EL0 by SCTLR_EL1.UCI.
11// VA = virtual address
12// PoC = point of coherency
13// PoU = point of unification
14
15// dc cvau
16void DataCacheLineCleanByVAToPoU(void* start, size_t size);
17
18// dc civac
19void DataCacheLineCleanAndInvalidateByVAToPoC(void* start, size_t size);
20
21// dc cvac
22void DataCacheLineCleanByVAToPoC(void* start, size_t size);
23
24// dc zva
25void DataCacheZeroByVA(void* start, size_t size);
26
27} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 705667080..5629980d9 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -120,6 +120,8 @@ add_library(core STATIC
120 file_sys/vfs_vector.h 120 file_sys/vfs_vector.h
121 file_sys/xts_archive.cpp 121 file_sys/xts_archive.cpp
122 file_sys/xts_archive.h 122 file_sys/xts_archive.h
123 frontend/applets/cabinet.cpp
124 frontend/applets/cabinet.h
123 frontend/applets/controller.cpp 125 frontend/applets/controller.cpp
124 frontend/applets/controller.h 126 frontend/applets/controller.h
125 frontend/applets/error.cpp 127 frontend/applets/error.cpp
@@ -312,6 +314,8 @@ add_library(core STATIC
312 hle/service/am/applet_ae.h 314 hle/service/am/applet_ae.h
313 hle/service/am/applet_oe.cpp 315 hle/service/am/applet_oe.cpp
314 hle/service/am/applet_oe.h 316 hle/service/am/applet_oe.h
317 hle/service/am/applets/applet_cabinet.cpp
318 hle/service/am/applets/applet_cabinet.h
315 hle/service/am/applets/applet_controller.cpp 319 hle/service/am/applets/applet_controller.cpp
316 hle/service/am/applets/applet_controller.h 320 hle/service/am/applets/applet_controller.h
317 hle/service/am/applets/applet_error.cpp 321 hle/service/am/applets/applet_error.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index afb7fb3a0..cb53d64ba 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -348,7 +348,6 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
348 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 348 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
349 config.unsafe_optimizations = true; 349 config.unsafe_optimizations = true;
350 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 350 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
351 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
352 config.fastmem_address_space_bits = 64; 351 config.fastmem_address_space_bits = 64;
353 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 352 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
354 } 353 }
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 339f971e6..1a8e02e6a 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); 27 const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; 28 std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
29 c(received_data); 29 c(received_data);
30 AsyncReceiveInto(r, buffer, c);
30 } 31 }
31
32 AsyncReceiveInto(r, buffer, c);
33 }); 32 });
34} 33}
35 34
35template <typename Callback>
36static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) {
37 acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) {
38 if (!error.failed()) {
39 c(peer_socket);
40 AsyncAccept(acceptor, c);
41 }
42 });
43}
44
36template <typename Readable, typename Buffer> 45template <typename Readable, typename Buffer>
37static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { 46static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
38 static_assert(std::is_trivial_v<Buffer>); 47 static_assert(std::is_trivial_v<Buffer>);
@@ -59,9 +68,7 @@ namespace Core {
59 68
60class DebuggerImpl : public DebuggerBackend { 69class DebuggerImpl : public DebuggerBackend {
61public: 70public:
62 explicit DebuggerImpl(Core::System& system_, u16 port) 71 explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} {
63 : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
64 frontend = std::make_unique<GDBStub>(*this, system);
65 InitializeServer(port); 72 InitializeServer(port);
66 } 73 }
67 74
@@ -70,39 +77,42 @@ public:
70 } 77 }
71 78
72 bool SignalDebugger(SignalInfo signal_info) { 79 bool SignalDebugger(SignalInfo signal_info) {
73 { 80 std::scoped_lock lk{connection_lock};
74 std::scoped_lock lk{connection_lock};
75 81
76 if (stopped) { 82 if (stopped || !state) {
77 // Do not notify the debugger about another event. 83 // Do not notify the debugger about another event.
78 // It should be ignored. 84 // It should be ignored.
79 return false; 85 return false;
80 }
81
82 // Set up the state.
83 stopped = true;
84 info = signal_info;
85 } 86 }
86 87
88 // Set up the state.
89 stopped = true;
90 state->info = signal_info;
91
87 // Write a single byte into the pipe to wake up the debug interface. 92 // Write a single byte into the pipe to wake up the debug interface.
88 boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); 93 boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
94
89 return true; 95 return true;
90 } 96 }
91 97
98 // These functions are callbacks from the frontend, and the lock will be held.
99 // There is no need to relock it.
100
92 std::span<const u8> ReadFromClient() override { 101 std::span<const u8> ReadFromClient() override {
93 return ReceiveInto(client_socket, client_data); 102 return ReceiveInto(state->client_socket, state->client_data);
94 } 103 }
95 104
96 void WriteToClient(std::span<const u8> data) override { 105 void WriteToClient(std::span<const u8> data) override {
97 boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); 106 boost::asio::write(state->client_socket,
107 boost::asio::buffer(data.data(), data.size_bytes()));
98 } 108 }
99 109
100 void SetActiveThread(Kernel::KThread* thread) override { 110 void SetActiveThread(Kernel::KThread* thread) override {
101 active_thread = thread; 111 state->active_thread = thread;
102 } 112 }
103 113
104 Kernel::KThread* GetActiveThread() override { 114 Kernel::KThread* GetActiveThread() override {
105 return active_thread; 115 return state->active_thread;
106 } 116 }
107 117
108private: 118private:
@@ -113,65 +123,78 @@ private:
113 123
114 // Run the connection thread. 124 // Run the connection thread.
115 connection_thread = std::jthread([&, port](std::stop_token stop_token) { 125 connection_thread = std::jthread([&, port](std::stop_token stop_token) {
126 Common::SetCurrentThreadName("Debugger");
127
116 try { 128 try {
117 // Initialize the listening socket and accept a new client. 129 // Initialize the listening socket and accept a new client.
118 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; 130 tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
119 tcp::acceptor acceptor{io_context, endpoint}; 131 tcp::acceptor acceptor{io_context, endpoint};
120 132
121 acceptor.async_accept(client_socket, [](const auto&) {}); 133 AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); });
122 io_context.run_one();
123 io_context.restart();
124 134
125 if (stop_token.stop_requested()) { 135 while (!stop_token.stop_requested() && io_context.run()) {
126 return;
127 } 136 }
128
129 ThreadLoop(stop_token);
130 } catch (const std::exception& ex) { 137 } catch (const std::exception& ex) {
131 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); 138 LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
132 } 139 }
133 }); 140 });
134 } 141 }
135 142
136 void ShutdownServer() { 143 void AcceptConnection(boost::asio::ip::tcp::socket&& peer) {
137 connection_thread.request_stop(); 144 LOG_INFO(Debug_GDBStub, "Accepting new peer connection");
138 io_context.stop();
139 connection_thread.join();
140 }
141 145
142 void ThreadLoop(std::stop_token stop_token) { 146 std::scoped_lock lk{connection_lock};
143 Common::SetCurrentThreadName("Debugger"); 147
148 // Ensure everything is stopped.
149 PauseEmulation();
150
151 // Set up the new frontend.
152 frontend = std::make_unique<GDBStub>(*this, system);
153
154 // Set the new state. This will tear down any existing state.
155 state = ConnectionState{
156 .client_socket{std::move(peer)},
157 .signal_pipe{io_context},
158 .info{},
159 .active_thread{},
160 .client_data{},
161 .pipe_data{},
162 };
144 163
145 // Set up the client signals for new data. 164 // Set up the client signals for new data.
146 AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); 165 AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); });
147 AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); 166 AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); });
148 167
149 // Set the active thread. 168 // Set the active thread.
150 UpdateActiveThread(); 169 UpdateActiveThread();
151 170
152 // Set up the frontend. 171 // Set up the frontend.
153 frontend->Connected(); 172 frontend->Connected();
173 }
154 174
155 // Main event loop. 175 void ShutdownServer() {
156 while (!stop_token.stop_requested() && io_context.run()) { 176 connection_thread.request_stop();
157 } 177 io_context.stop();
178 connection_thread.join();
158 } 179 }
159 180
160 void PipeData(std::span<const u8> data) { 181 void PipeData(std::span<const u8> data) {
161 switch (info.type) { 182 std::scoped_lock lk{connection_lock};
183
184 switch (state->info.type) {
162 case SignalType::Stopped: 185 case SignalType::Stopped:
163 case SignalType::Watchpoint: 186 case SignalType::Watchpoint:
164 // Stop emulation. 187 // Stop emulation.
165 PauseEmulation(); 188 PauseEmulation();
166 189
167 // Notify the client. 190 // Notify the client.
168 active_thread = info.thread; 191 state->active_thread = state->info.thread;
169 UpdateActiveThread(); 192 UpdateActiveThread();
170 193
171 if (info.type == SignalType::Watchpoint) { 194 if (state->info.type == SignalType::Watchpoint) {
172 frontend->Watchpoint(active_thread, *info.watchpoint); 195 frontend->Watchpoint(state->active_thread, *state->info.watchpoint);
173 } else { 196 } else {
174 frontend->Stopped(active_thread); 197 frontend->Stopped(state->active_thread);
175 } 198 }
176 199
177 break; 200 break;
@@ -179,8 +202,8 @@ private:
179 frontend->ShuttingDown(); 202 frontend->ShuttingDown();
180 203
181 // Wait for emulation to shut down gracefully now. 204 // Wait for emulation to shut down gracefully now.
182 signal_pipe.close(); 205 state->signal_pipe.close();
183 client_socket.shutdown(boost::asio::socket_base::shutdown_both); 206 state->client_socket.shutdown(boost::asio::socket_base::shutdown_both);
184 LOG_INFO(Debug_GDBStub, "Shut down server"); 207 LOG_INFO(Debug_GDBStub, "Shut down server");
185 208
186 break; 209 break;
@@ -188,17 +211,16 @@ private:
188 } 211 }
189 212
190 void ClientData(std::span<const u8> data) { 213 void ClientData(std::span<const u8> data) {
214 std::scoped_lock lk{connection_lock};
215
191 const auto actions{frontend->ClientData(data)}; 216 const auto actions{frontend->ClientData(data)};
192 for (const auto action : actions) { 217 for (const auto action : actions) {
193 switch (action) { 218 switch (action) {
194 case DebuggerAction::Interrupt: { 219 case DebuggerAction::Interrupt: {
195 { 220 stopped = true;
196 std::scoped_lock lk{connection_lock};
197 stopped = true;
198 }
199 PauseEmulation(); 221 PauseEmulation();
200 UpdateActiveThread(); 222 UpdateActiveThread();
201 frontend->Stopped(active_thread); 223 frontend->Stopped(state->active_thread);
202 break; 224 break;
203 } 225 }
204 case DebuggerAction::Continue: 226 case DebuggerAction::Continue:
@@ -206,15 +228,15 @@ private:
206 break; 228 break;
207 case DebuggerAction::StepThreadUnlocked: 229 case DebuggerAction::StepThreadUnlocked:
208 MarkResumed([&] { 230 MarkResumed([&] {
209 active_thread->SetStepState(Kernel::StepState::StepPending); 231 state->active_thread->SetStepState(Kernel::StepState::StepPending);
210 active_thread->Resume(Kernel::SuspendType::Debug); 232 state->active_thread->Resume(Kernel::SuspendType::Debug);
211 ResumeEmulation(active_thread); 233 ResumeEmulation(state->active_thread);
212 }); 234 });
213 break; 235 break;
214 case DebuggerAction::StepThreadLocked: { 236 case DebuggerAction::StepThreadLocked: {
215 MarkResumed([&] { 237 MarkResumed([&] {
216 active_thread->SetStepState(Kernel::StepState::StepPending); 238 state->active_thread->SetStepState(Kernel::StepState::StepPending);
217 active_thread->Resume(Kernel::SuspendType::Debug); 239 state->active_thread->Resume(Kernel::SuspendType::Debug);
218 }); 240 });
219 break; 241 break;
220 } 242 }
@@ -254,15 +276,14 @@ private:
254 template <typename Callback> 276 template <typename Callback>
255 void MarkResumed(Callback&& cb) { 277 void MarkResumed(Callback&& cb) {
256 Kernel::KScopedSchedulerLock sl{system.Kernel()}; 278 Kernel::KScopedSchedulerLock sl{system.Kernel()};
257 std::scoped_lock cl{connection_lock};
258 stopped = false; 279 stopped = false;
259 cb(); 280 cb();
260 } 281 }
261 282
262 void UpdateActiveThread() { 283 void UpdateActiveThread() {
263 const auto& threads{ThreadList()}; 284 const auto& threads{ThreadList()};
264 if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { 285 if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) {
265 active_thread = threads[0]; 286 state->active_thread = threads[0];
266 } 287 }
267 } 288 }
268 289
@@ -274,18 +295,22 @@ private:
274 System& system; 295 System& system;
275 std::unique_ptr<DebuggerFrontend> frontend; 296 std::unique_ptr<DebuggerFrontend> frontend;
276 297
298 boost::asio::io_context io_context;
277 std::jthread connection_thread; 299 std::jthread connection_thread;
278 std::mutex connection_lock; 300 std::mutex connection_lock;
279 boost::asio::io_context io_context;
280 boost::process::async_pipe signal_pipe;
281 boost::asio::ip::tcp::socket client_socket;
282 301
283 SignalInfo info; 302 struct ConnectionState {
284 Kernel::KThread* active_thread; 303 boost::asio::ip::tcp::socket client_socket;
285 bool pipe_data; 304 boost::process::async_pipe signal_pipe;
286 bool stopped; 305
306 SignalInfo info;
307 Kernel::KThread* active_thread;
308 std::array<u8, 4096> client_data;
309 bool pipe_data;
310 };
287 311
288 std::array<u8, 4096> client_data; 312 std::optional<ConnectionState> state{};
313 bool stopped{};
289}; 314};
290 315
291Debugger::Debugger(Core::System& system, u16 port) { 316Debugger::Debugger(Core::System& system, u16 port) {
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index 884229c77..a64a9ac64 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) {
606 } else if (command.starts_with("StartNoAckMode")) { 606 } else if (command.starts_with("StartNoAckMode")) {
607 no_ack = true; 607 no_ack = true;
608 SendReply(GDB_STUB_REPLY_OK); 608 SendReply(GDB_STUB_REPLY_OK);
609 } else if (command.starts_with("Rcmd,")) {
610 HandleRcmd(Common::HexStringToVector(command.substr(5), false));
609 } else { 611 } else {
610 SendReply(GDB_STUB_REPLY_EMPTY); 612 SendReply(GDB_STUB_REPLY_EMPTY);
611 } 613 }
@@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>&
645 } 647 }
646} 648}
647 649
650constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{
651 {"----- Free -----", Kernel::Svc::MemoryState::Free},
652 {"Io ", Kernel::Svc::MemoryState::Io},
653 {"Static ", Kernel::Svc::MemoryState::Static},
654 {"Code ", Kernel::Svc::MemoryState::Code},
655 {"CodeData ", Kernel::Svc::MemoryState::CodeData},
656 {"Normal ", Kernel::Svc::MemoryState::Normal},
657 {"Shared ", Kernel::Svc::MemoryState::Shared},
658 {"AliasCode ", Kernel::Svc::MemoryState::AliasCode},
659 {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData},
660 {"Ipc ", Kernel::Svc::MemoryState::Ipc},
661 {"Stack ", Kernel::Svc::MemoryState::Stack},
662 {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal},
663 {"Transfered ", Kernel::Svc::MemoryState::Transfered},
664 {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered},
665 {"SharedCode ", Kernel::Svc::MemoryState::SharedCode},
666 {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible},
667 {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc},
668 {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc},
669 {"Kernel ", Kernel::Svc::MemoryState::Kernel},
670 {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode},
671 {"CodeOut ", Kernel::Svc::MemoryState::CodeOut},
672 {"Coverage ", Kernel::Svc::MemoryState::Coverage},
673}};
674
675static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) {
676 for (size_t i = 0; i < MemoryStateNames.size(); i++) {
677 if (std::get<1>(MemoryStateNames[i]) == state) {
678 return std::get<0>(MemoryStateNames[i]);
679 }
680 }
681 return "Unknown ";
682}
683
684static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) {
685 if (info.state == Kernel::Svc::MemoryState::Free) {
686 return " ";
687 }
688
689 switch (info.permission) {
690 case Kernel::Svc::MemoryPermission::ReadExecute:
691 return "r-x";
692 case Kernel::Svc::MemoryPermission::Read:
693 return "r--";
694 case Kernel::Svc::MemoryPermission::ReadWrite:
695 return "rw-";
696 default:
697 return "---";
698 }
699}
700
701static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) {
702 Kernel::Svc::MemoryInfo mem_info;
703 VAddr cur_addr{base};
704
705 // Expect: r-x Code (.text)
706 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
707 cur_addr = mem_info.base_address + mem_info.size;
708 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
709 mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
710 return cur_addr - 1;
711 }
712
713 // Expect: r-- Code (.rodata)
714 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
715 cur_addr = mem_info.base_address + mem_info.size;
716 if (mem_info.state != Kernel::Svc::MemoryState::Code ||
717 mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
718 return cur_addr - 1;
719 }
720
721 // Expect: rw- CodeData (.data)
722 mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
723 cur_addr = mem_info.base_address + mem_info.size;
724 return cur_addr - 1;
725}
726
727void GDBStub::HandleRcmd(const std::vector<u8>& command) {
728 std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()};
729 std::string reply;
730
731 auto* process = system.CurrentProcess();
732 auto& page_table = process->PageTable();
733
734 if (command_str == "get info") {
735 Loader::AppLoader::Modules modules;
736 system.GetAppLoader().ReadNSOModules(modules);
737
738 reply = fmt::format("Process: {:#x} ({})\n"
739 "Program Id: {:#018x}\n",
740 process->GetProcessID(), process->GetName(), process->GetProgramID());
741 reply +=
742 fmt::format("Layout:\n"
743 " Alias: {:#012x} - {:#012x}\n"
744 " Heap: {:#012x} - {:#012x}\n"
745 " Aslr: {:#012x} - {:#012x}\n"
746 " Stack: {:#012x} - {:#012x}\n"
747 "Modules:\n",
748 page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(),
749 page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(),
750 page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(),
751 page_table.GetStackRegionStart(), page_table.GetStackRegionEnd());
752
753 for (const auto& [vaddr, name] : modules) {
754 reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr,
755 GetModuleEnd(page_table, vaddr), name);
756 }
757 } else if (command_str == "get mappings") {
758 reply = "Mappings:\n";
759 VAddr cur_addr = 0;
760
761 while (true) {
762 using MemoryAttribute = Kernel::Svc::MemoryAttribute;
763
764 auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo();
765
766 if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible ||
767 mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) {
768 const char* state = GetMemoryStateName(mem_info.state);
769 const char* perm = GetMemoryPermissionString(mem_info);
770
771 const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-';
772 const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-';
773 const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-';
774 const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-';
775
776 reply +=
777 fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n",
778 mem_info.base_address, mem_info.base_address + mem_info.size - 1,
779 perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count);
780 }
781
782 const uintptr_t next_address = mem_info.base_address + mem_info.size;
783 if (next_address <= cur_addr) {
784 break;
785 }
786
787 cur_addr = next_address;
788 }
789 } else if (command_str == "help") {
790 reply = "Commands:\n get info\n get mappings\n";
791 } else {
792 reply = "Unknown command.\nCommands:\n get info\n get mappings\n";
793 }
794
795 std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()};
796 SendReply(Common::HexToString(reply_span, false));
797}
798
648Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { 799Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
649 const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; 800 const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
650 for (auto* thread : threads) { 801 for (auto* thread : threads) {
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
index 0b0f56e4b..368197920 100644
--- a/src/core/debugger/gdbstub.h
+++ b/src/core/debugger/gdbstub.h
@@ -32,6 +32,7 @@ private:
32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); 32 void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); 33 void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
34 void HandleQuery(std::string_view command); 34 void HandleQuery(std::string_view command);
35 void HandleRcmd(const std::vector<u8>& command);
35 void HandleBreakpointInsert(std::string_view command); 36 void HandleBreakpointInsert(std::string_view command);
36 void HandleBreakpointRemove(std::string_view command); 37 void HandleBreakpointRemove(std::string_view command);
37 std::vector<char>::const_iterator CommandEnd() const; 38 std::vector<char>::const_iterator CommandEnd() const;
diff --git a/src/core/frontend/applets/cabinet.cpp b/src/core/frontend/applets/cabinet.cpp
new file mode 100644
index 000000000..26c7fefe3
--- /dev/null
+++ b/src/core/frontend/applets/cabinet.cpp
@@ -0,0 +1,20 @@
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 "core/frontend/applets/cabinet.h"
6
7#include <thread>
8
9namespace Core::Frontend {
10
11CabinetApplet::~CabinetApplet() = default;
12
13void DefaultCabinetApplet::ShowCabinetApplet(
14 const CabinetCallback& callback, const CabinetParameters& parameters,
15 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
16 LOG_WARNING(Service_AM, "(STUBBED) called");
17 callback(false, {});
18}
19
20} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/cabinet.h b/src/core/frontend/applets/cabinet.h
new file mode 100644
index 000000000..c28a235c1
--- /dev/null
+++ b/src/core/frontend/applets/cabinet.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <functional>
7#include "core/hle/service/nfp/nfp_types.h"
8
9namespace Service::NFP {
10class NfpDevice;
11} // namespace Service::NFP
12
13namespace Core::Frontend {
14
15struct CabinetParameters {
16 Service::NFP::TagInfo tag_info;
17 Service::NFP::RegisterInfo register_info;
18 Service::NFP::CabinetMode mode;
19};
20
21using CabinetCallback = std::function<void(bool, const std::string&)>;
22
23class CabinetApplet {
24public:
25 virtual ~CabinetApplet();
26 virtual void ShowCabinetApplet(const CabinetCallback& callback,
27 const CabinetParameters& parameters,
28 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const = 0;
29};
30
31class DefaultCabinetApplet final : public CabinetApplet {
32public:
33 void ShowCabinetApplet(const CabinetCallback& callback, const CabinetParameters& parameters,
34 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
35};
36
37} // namespace Core::Frontend
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index aac45907d..fb7e5802a 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
19} 19}
20 20
21void EmulatedConsole::SetTouchParams() { 21void EmulatedConsole::SetTouchParams() {
22 // TODO(german77): Support any number of fingers
23 std::size_t index = 0; 22 std::size_t index = 0;
24 23
25 // Hardcode mouse, touchscreen and cemuhook parameters 24 // We can't use mouse as touch if native mouse is enabled
26 if (!Settings::values.mouse_enabled) { 25 if (!Settings::values.mouse_enabled) {
27 // We can't use mouse as touch if native mouse is enabled
28 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"}; 26 touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
29 } 27 }
30 28
31 touch_params[index++] = 29 touch_params[index++] =
32 Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"}; 30 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
33 touch_params[index++] = 31 touch_params[index++] =
34 Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"}; 32 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
35 touch_params[index++] = 33
36 Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"}; 34 for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
37 touch_params[index++] = 35 Common::ParamPackage touchscreen_param{};
38 Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"}; 36 touchscreen_param.Set("engine", "touch");
39 touch_params[index++] = 37 touchscreen_param.Set("axis_x", i * 2);
40 Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"}; 38 touchscreen_param.Set("axis_y", (i * 2) + 1);
41 touch_params[index++] = 39 touchscreen_param.Set("button", i);
42 Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"}; 40 touch_params[index++] = touchscreen_param;
41 }
43 42
44 const auto button_index = 43 const auto button_index =
45 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue()); 44 static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
47 46
48 // Map the rest of the fingers from touch from button configuration 47 // Map the rest of the fingers from touch from button configuration
49 for (const auto& config_entry : touch_buttons) { 48 for (const auto& config_entry : touch_buttons) {
50 if (index >= touch_params.size()) { 49 if (index >= MaxTouchDevices) {
51 continue; 50 continue;
52 } 51 }
53 Common::ParamPackage params{config_entry}; 52 Common::ParamPackage params{config_entry};
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
60 touch_button_params.Set("button", params.Serialize()); 59 touch_button_params.Set("button", params.Serialize());
61 touch_button_params.Set("x", x); 60 touch_button_params.Set("x", x);
62 touch_button_params.Set("y", y); 61 touch_button_params.Set("y", y);
63 touch_button_params.Set("touch_id", static_cast<int>(index));
64 touch_params[index] = touch_button_params; 62 touch_params[index] = touch_button_params;
65 index++; 63 index++;
66 } 64 }
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
178} 176}
179 177
180void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) { 178void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
181 if (index >= console.touch_values.size()) { 179 if (index >= MaxTouchDevices) {
182 return; 180 return;
183 } 181 }
184 std::unique_lock lock{mutex}; 182 std::unique_lock lock{mutex};
185 183
186 console.touch_values[index] = TransformToTouch(callback); 184 const auto touch_input = TransformToTouch(callback);
185 auto touch_index = GetIndexFromFingerId(index);
186 bool is_new_input = false;
187
188 if (!touch_index.has_value() && touch_input.pressed.value) {
189 touch_index = GetNextFreeIndex();
190 is_new_input = true;
191 }
192
193 // No free entries or invalid state. Ignore input
194 if (!touch_index.has_value()) {
195 return;
196 }
197
198 auto& touch_value = console.touch_values[touch_index.value()];
199
200 if (is_new_input) {
201 touch_value.pressed.value = true;
202 touch_value.id = static_cast<u32>(index);
203 }
204
205 touch_value.x = touch_input.x;
206 touch_value.y = touch_input.y;
207
208 if (!touch_input.pressed.value) {
209 touch_value.pressed.value = false;
210 }
187 211
188 if (is_configuring) { 212 if (is_configuring) {
189 lock.unlock(); 213 lock.unlock();
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
191 return; 215 return;
192 } 216 }
193 217
194 // TODO(german77): Remap touch id in sequential order 218 // Touch outside allowed range. Ignore input
195 console.touch_state[index] = { 219 if (touch_index.value() >= MaxActiveTouchInputs) {
196 .position = {console.touch_values[index].x.value, console.touch_values[index].y.value}, 220 return;
197 .id = static_cast<u32>(console.touch_values[index].id), 221 }
198 .pressed = console.touch_values[index].pressed.value, 222
223 console.touch_state[touch_index.value()] = {
224 .position = {touch_value.x.value, touch_value.y.value},
225 .id = static_cast<u32>(touch_index.value()),
226 .pressed = touch_input.pressed.value,
199 }; 227 };
200 228
201 lock.unlock(); 229 lock.unlock();
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
222 return console.touch_state; 250 return console.touch_state;
223} 251}
224 252
253std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
254 for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
255 const auto& finger = console.touch_values[index];
256 if (!finger.pressed.value) {
257 continue;
258 }
259 if (finger.id == static_cast<int>(finger_id)) {
260 return index;
261 }
262 }
263 return std::nullopt;
264}
265
266std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
267 for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
268 if (!console.touch_values[index].pressed.value) {
269 return index;
270 }
271 }
272 return std::nullopt;
273}
274
225void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { 275void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
226 std::scoped_lock lock{callback_mutex}; 276 std::scoped_lock lock{callback_mutex};
227 for (const auto& poller_pair : callback_list) { 277 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 1c510cd19..697ecd2d6 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -7,6 +7,7 @@
7#include <functional> 7#include <functional>
8#include <memory> 8#include <memory>
9#include <mutex> 9#include <mutex>
10#include <optional>
10#include <unordered_map> 11#include <unordered_map>
11 12
12#include "common/common_funcs.h" 13#include "common/common_funcs.h"
@@ -20,6 +21,8 @@
20#include "core/hid/motion_input.h" 21#include "core/hid/motion_input.h"
21 22
22namespace Core::HID { 23namespace Core::HID {
24static constexpr std::size_t MaxTouchDevices = 32;
25static constexpr std::size_t MaxActiveTouchInputs = 16;
23 26
24struct ConsoleMotionInfo { 27struct ConsoleMotionInfo {
25 Common::Input::MotionStatus raw_status{}; 28 Common::Input::MotionStatus raw_status{};
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
27}; 30};
28 31
29using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>; 32using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
30using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>; 33using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
31 34
32using ConsoleMotionParams = Common::ParamPackage; 35using ConsoleMotionParams = Common::ParamPackage;
33using TouchParams = std::array<Common::ParamPackage, 16>; 36using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
34 37
35using ConsoleMotionValues = ConsoleMotionInfo; 38using ConsoleMotionValues = ConsoleMotionInfo;
36using TouchValues = std::array<Common::Input::TouchStatus, 16>; 39using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
37 40
38struct TouchFinger { 41struct TouchFinger {
39 u64 last_touch{}; 42 u64 last_touch{};
@@ -55,7 +58,7 @@ struct ConsoleMotion {
55 bool is_at_rest{}; 58 bool is_at_rest{};
56}; 59};
57 60
58using TouchFingerState = std::array<TouchFinger, 16>; 61using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
59 62
60struct ConsoleStatus { 63struct ConsoleStatus {
61 // Data from input_common 64 // Data from input_common
@@ -166,6 +169,10 @@ private:
166 */ 169 */
167 void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index); 170 void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
168 171
172 std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
173
174 std::optional<std::size_t> GetNextFreeIndex() const;
175
169 /** 176 /**
170 * Triggers a callback that something has changed on the console status 177 * Triggers a callback that something has changed on the console status
171 * @param type Input type of the event to trigger 178 * @param type Input type of the event to trigger
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 5d8b75b50..502692875 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
200 x = std::clamp(x, 0.0f, 1.0f); 200 x = std::clamp(x, 0.0f, 1.0f);
201 y = std::clamp(y, 0.0f, 1.0f); 201 y = std::clamp(y, 0.0f, 1.0f);
202 202
203 // Limit id to maximum number of fingers
204 status.id = std::clamp(status.id, 0, 16);
205
206 if (status.pressed.inverted) { 203 if (status.pressed.inverted) {
207 status.pressed.value = !status.pressed.value; 204 status.pressed.value = !status.pressed.value;
208 } 205 }
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index bda098511..7b363eb1e 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
243 // If we somehow get an invalid type, abort. 243 // If we somehow get an invalid type, abort.
244 default: 244 default:
245 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); 245 ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
246 break;
246 } 247 }
247 248
248 // If we've hit the end of a gap, free it. 249 // If we've hit the end of a gap, free it.
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 5387bf5fe..612fc76fa 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size,
2301 break; 2301 break;
2302 default: 2302 default:
2303 ASSERT(false); 2303 ASSERT(false);
2304 break;
2304 } 2305 }
2305 } 2306 }
2306 2307
@@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_
2803 break; 2804 break;
2804 default: 2805 default:
2805 ASSERT(false); 2806 ASSERT(false);
2807 break;
2806 } 2808 }
2807 2809
2808 addr += size; 2810 addr += size;
@@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm,
2838 break; 2840 break;
2839 default: 2841 default:
2840 ASSERT(false); 2842 ASSERT(false);
2843 break;
2841 } 2844 }
2842 R_SUCCEED(); 2845 R_SUCCEED();
2843} 2846}
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index 950850291..f1ca785d7 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -320,6 +320,9 @@ public:
320 constexpr VAddr GetAliasCodeRegionStart() const { 320 constexpr VAddr GetAliasCodeRegionStart() const {
321 return m_alias_code_region_start; 321 return m_alias_code_region_start;
322 } 322 }
323 constexpr VAddr GetAliasCodeRegionEnd() const {
324 return m_alias_code_region_end;
325 }
323 constexpr VAddr GetAliasCodeRegionSize() const { 326 constexpr VAddr GetAliasCodeRegionSize() const {
324 return m_alias_code_region_end - m_alias_code_region_start; 327 return m_alias_code_region_end - m_alias_code_region_start;
325 } 328 }
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 55a9c5fae..d1dc62401 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
395 395
396 default: 396 default:
397 ASSERT(false); 397 ASSERT(false);
398 break;
398 } 399 }
399 400
400 // Create TLS region 401 // Create TLS region
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 9962ad171..e520cab47 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -2701,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou
2701 return ResultSuccess; 2701 return ResultSuccess;
2702} 2702}
2703 2703
2704static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, 2704static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
2705 [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, 2705 u64 size) {
2706 [[maybe_unused]] u32 size) { 2706 // Validate address/size.
2707 // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, 2707 R_UNLESS(size > 0, ResultInvalidSize);
2708 // as all emulation is done in the same cache level in host architecture, thus data cache 2708 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
2709 // does not need flushing. 2709 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
2710 LOG_DEBUG(Kernel_SVC, "called"); 2710
2711 return ResultSuccess; 2711 // Get the process from its handle.
2712 KScopedAutoObject process =
2713 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
2714 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
2715
2716 // Verify the region is within range.
2717 auto& page_table = process->PageTable();
2718 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
2719
2720 // Perform the operation.
2721 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
2712} 2722}
2713 2723
2714namespace { 2724namespace {
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 272c54cf7..3730937fe 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) {
722 FuncReturn(system, retval); 722 FuncReturn(system, retval);
723} 723}
724 724
725// Used by Invalidate/Store/FlushProcessDataCache32
726template <Result func(Core::System&, Handle, u64, u64)>
727void SvcWrap32(Core::System& system) {
728 const u64 address = (Param(system, 3) << 32) | Param(system, 2);
729 const u64 size = (Param(system, 4) << 32) | Param(system, 1);
730 FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
731}
732
725} // namespace Kernel 733} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 56c990728..240f06689 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -28,30 +28,49 @@ enum class ErrorModule : u32 {
28 Loader = 9, 28 Loader = 9,
29 CMIF = 10, 29 CMIF = 10,
30 HIPC = 11, 30 HIPC = 11,
31 TMA = 12,
32 DMNT = 13,
33 GDS = 14,
31 PM = 15, 34 PM = 15,
32 NS = 16, 35 NS = 16,
36 BSDSockets = 17,
33 HTC = 18, 37 HTC = 18,
38 TSC = 19,
34 NCMContent = 20, 39 NCMContent = 20,
35 SM = 21, 40 SM = 21,
36 RO = 22, 41 RO = 22,
42 GC = 23,
37 SDMMC = 24, 43 SDMMC = 24,
38 OVLN = 25, 44 OVLN = 25,
39 SPL = 26, 45 SPL = 26,
46 Socket = 27,
47 HTCLOW = 29,
48 DDSF = 30,
49 HTCFS = 31,
50 Async = 32,
51 Util = 33,
52 TIPC = 35,
53 ANIF = 37,
40 ETHC = 100, 54 ETHC = 100,
41 I2C = 101, 55 I2C = 101,
42 GPIO = 102, 56 GPIO = 102,
43 UART = 103, 57 UART = 103,
58 CPAD = 104,
44 Settings = 105, 59 Settings = 105,
60 FTM = 106,
45 WLAN = 107, 61 WLAN = 107,
46 XCD = 108, 62 XCD = 108,
63 TMP451 = 109,
47 NIFM = 110, 64 NIFM = 110,
48 Hwopus = 111, 65 Hwopus = 111,
66 LSM6DS3 = 112,
49 Bluetooth = 113, 67 Bluetooth = 113,
50 VI = 114, 68 VI = 114,
51 NFP = 115, 69 NFP = 115,
52 Time = 116, 70 Time = 116,
53 FGM = 117, 71 FGM = 117,
54 OE = 118, 72 OE = 118,
73 BH1730FVC = 119,
55 PCIe = 120, 74 PCIe = 120,
56 Friends = 121, 75 Friends = 121,
57 BCAT = 122, 76 BCAT = 122,
@@ -65,7 +84,7 @@ enum class ErrorModule : u32 {
65 AHID = 130, 84 AHID = 130,
66 Qlaunch = 132, 85 Qlaunch = 132,
67 PCV = 133, 86 PCV = 133,
68 OMM = 134, 87 USBPD = 134,
69 BPC = 135, 88 BPC = 135,
70 PSM = 136, 89 PSM = 136,
71 NIM = 137, 90 NIM = 137,
@@ -75,18 +94,22 @@ enum class ErrorModule : u32 {
75 NSD = 141, 94 NSD = 141,
76 PCTL = 142, 95 PCTL = 142,
77 BTM = 143, 96 BTM = 143,
97 LA = 144,
78 ETicket = 145, 98 ETicket = 145,
79 NGC = 146, 99 NGC = 146,
80 ERPT = 147, 100 ERPT = 147,
81 APM = 148, 101 APM = 148,
102 CEC = 149,
82 Profiler = 150, 103 Profiler = 150,
83 ErrorUpload = 151, 104 ErrorUpload = 151,
105 LIDBE = 152,
84 Audio = 153, 106 Audio = 153,
85 NPNS = 154, 107 NPNS = 154,
86 NPNSHTTPSTREAM = 155, 108 NPNSHTTPSTREAM = 155,
87 ARP = 157, 109 ARP = 157,
88 SWKBD = 158, 110 SWKBD = 158,
89 BOOT = 159, 111 BOOT = 159,
112 NetDiag = 160,
90 NFCMifare = 161, 113 NFCMifare = 161,
91 UserlandAssert = 162, 114 UserlandAssert = 162,
92 Fatal = 163, 115 Fatal = 163,
@@ -94,17 +117,68 @@ enum class ErrorModule : u32 {
94 SPSM = 165, 117 SPSM = 165,
95 BGTC = 167, 118 BGTC = 167,
96 UserlandCrash = 168, 119 UserlandCrash = 168,
120 SASBUS = 169,
121 PI = 170,
122 AudioCtrl = 172,
123 LBL = 173,
124 JIT = 175,
125 HDCP = 176,
126 OMM = 177,
127 PDM = 178,
128 OLSC = 179,
97 SREPO = 180, 129 SREPO = 180,
98 Dauth = 181, 130 Dauth = 181,
131 STDFU = 182,
132 DBG = 183,
133 DHCPS = 186,
134 SPI = 187,
135 AVM = 188,
136 PWM = 189,
137 RTC = 191,
138 Regulator = 192,
139 LED = 193,
140 SIO = 195,
141 PCM = 196,
142 CLKRST = 197,
143 POWCTL = 198,
144 AudioOld = 201,
99 HID = 202, 145 HID = 202,
100 LDN = 203, 146 LDN = 203,
147 CS = 204,
101 Irsensor = 205, 148 Irsensor = 205,
102 Capture = 206, 149 Capture = 206,
103 Manu = 208, 150 Manu = 208,
104 ATK = 209, 151 ATK = 209,
152 WEB = 210,
153 LCS = 211,
105 GRC = 212, 154 GRC = 212,
155 Repair = 213,
156 Album = 214,
157 RID = 215,
106 Migration = 216, 158 Migration = 216,
107 MigrationLdcServ = 217, 159 MigrationLdcServ = 217,
160 HIDBUS = 218,
161 ENS = 219,
162 WebSocket = 223,
163 DCDMTP = 227,
164 PGL = 228,
165 Notification = 229,
166 INS = 230,
167 LP2P = 231,
168 RCD = 232,
169 LCM40607 = 233,
170 PRC = 235,
171 TMAHTC = 237,
172 ECTX = 238,
173 MNPP = 239,
174 HSHL = 240,
175 CAPMTP = 242,
176 DP2HDMI = 244,
177 Cradle = 245,
178 SProfile = 246,
179 NDRM = 250,
180 TSPM = 499,
181 DevMenu = 500,
108 GeneralWebApplet = 800, 182 GeneralWebApplet = 800,
109 WifiWebAuthApplet = 809, 183 WifiWebAuthApplet = 809,
110 WhitelistedApplet = 810, 184 WhitelistedApplet = 810,
diff --git a/src/core/hle/service/am/applets/applet_cabinet.cpp b/src/core/hle/service/am/applets/applet_cabinet.cpp
new file mode 100644
index 000000000..d0969b0f1
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_cabinet.cpp
@@ -0,0 +1,177 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/frontend/applets/cabinet.h"
8#include "core/hid/hid_core.h"
9#include "core/hle/kernel/k_event.h"
10#include "core/hle/kernel/k_readable_event.h"
11#include "core/hle/service/am/am.h"
12#include "core/hle/service/am/applets/applet_cabinet.h"
13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/nfp/nfp_device.h"
15
16namespace Service::AM::Applets {
17
18Cabinet::Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
19 const Core::Frontend::CabinetApplet& frontend_)
20 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_}, service_context{
21 system_,
22 "CabinetApplet"} {
23
24 availability_change_event =
25 service_context.CreateEvent("CabinetApplet:AvailabilityChangeEvent");
26}
27
28Cabinet::~Cabinet() = default;
29
30void Cabinet::Initialize() {
31 Applet::Initialize();
32
33 LOG_INFO(Service_HID, "Initializing Cabinet Applet.");
34
35 LOG_DEBUG(Service_HID,
36 "Initializing Applet with common_args: arg_version={}, lib_version={}, "
37 "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
38 common_args.arguments_version, common_args.library_version,
39 common_args.play_startup_sound, common_args.size, common_args.system_tick,
40 common_args.theme_color);
41
42 const auto storage = broker.PopNormalDataToApplet();
43 ASSERT(storage != nullptr);
44
45 const auto applet_input_data = storage->GetData();
46 ASSERT(applet_input_data.size() >= sizeof(StartParamForAmiiboSettings));
47
48 std::memcpy(&applet_input_common, applet_input_data.data(),
49 sizeof(StartParamForAmiiboSettings));
50}
51
52bool Cabinet::TransactionComplete() const {
53 return is_complete;
54}
55
56Result Cabinet::GetStatus() const {
57 return ResultSuccess;
58}
59
60void Cabinet::ExecuteInteractive() {
61 ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
62}
63
64void Cabinet::Execute() {
65 if (is_complete) {
66 return;
67 }
68
69 const auto callback = [this](bool apply_changes, const std::string& amiibo_name) {
70 DisplayCompleted(apply_changes, amiibo_name);
71 };
72
73 // TODO: listen on all controllers
74 if (nfp_device == nullptr) {
75 nfp_device = std::make_shared<Service::NFP::NfpDevice>(
76 system.HIDCore().GetFirstNpadId(), system, service_context, availability_change_event);
77 nfp_device->Initialize();
78 nfp_device->StartDetection(Service::NFP::TagProtocol::All);
79 }
80
81 const Core::Frontend::CabinetParameters parameters{
82 .tag_info = applet_input_common.tag_info,
83 .register_info = applet_input_common.register_info,
84 .mode = applet_input_common.applet_mode,
85 };
86
87 switch (applet_input_common.applet_mode) {
88 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
89 case Service::NFP::CabinetMode::StartGameDataEraser:
90 case Service::NFP::CabinetMode::StartRestorer:
91 case Service::NFP::CabinetMode::StartFormatter:
92 frontend.ShowCabinetApplet(callback, parameters, nfp_device);
93 break;
94 default:
95 UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
96 DisplayCompleted(false, {});
97 break;
98 }
99}
100
101void Cabinet::DisplayCompleted(bool apply_changes, std::string_view amiibo_name) {
102 Service::Mii::MiiManager manager;
103 ReturnValueForAmiiboSettings applet_output{};
104
105 if (!apply_changes) {
106 Cancel();
107 }
108
109 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
110 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
111 Cancel();
112 }
113
114 if (nfp_device->GetCurrentState() == Service::NFP::DeviceState::TagFound) {
115 nfp_device->Mount(Service::NFP::MountTarget::All);
116 }
117
118 switch (applet_input_common.applet_mode) {
119 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings: {
120 Service::NFP::AmiiboName name{};
121 std::memcpy(name.data(), amiibo_name.data(), std::min(amiibo_name.size(), name.size() - 1));
122 nfp_device->SetNicknameAndOwner(name);
123 break;
124 }
125 case Service::NFP::CabinetMode::StartGameDataEraser:
126 nfp_device->DeleteApplicationArea();
127 break;
128 case Service::NFP::CabinetMode::StartRestorer:
129 nfp_device->RestoreAmiibo();
130 break;
131 case Service::NFP::CabinetMode::StartFormatter:
132 nfp_device->DeleteAllData();
133 break;
134 default:
135 UNIMPLEMENTED_MSG("Unknown CabinetMode={}", applet_input_common.applet_mode);
136 break;
137 }
138
139 applet_output.device_handle = applet_input_common.device_handle;
140 applet_output.result = CabinetResult::Cancel;
141 const auto reg_result = nfp_device->GetRegisterInfo(applet_output.register_info);
142 const auto tag_result = nfp_device->GetTagInfo(applet_output.tag_info);
143 nfp_device->Finalize();
144
145 if (reg_result.IsSuccess()) {
146 applet_output.result |= CabinetResult::RegisterInfo;
147 }
148
149 if (tag_result.IsSuccess()) {
150 applet_output.result |= CabinetResult::TagInfo;
151 }
152
153 std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
154 std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
155
156 is_complete = true;
157
158 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
159 broker.SignalStateChanged();
160}
161
162void Cabinet::Cancel() {
163 ReturnValueForAmiiboSettings applet_output{};
164 applet_output.device_handle = applet_input_common.device_handle;
165 applet_output.result = CabinetResult::Cancel;
166 nfp_device->Finalize();
167
168 std::vector<u8> out_data(sizeof(ReturnValueForAmiiboSettings));
169 std::memcpy(out_data.data(), &applet_output, sizeof(ReturnValueForAmiiboSettings));
170
171 is_complete = true;
172
173 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
174 broker.SignalStateChanged();
175}
176
177} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h
new file mode 100644
index 000000000..84197a807
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_cabinet.h
@@ -0,0 +1,104 @@
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
8#include "core/hle/result.h"
9#include "core/hle/service/am/applets/applets.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/nfp/nfp_types.h"
12
13namespace Kernel {
14class KEvent;
15class KReadableEvent;
16} // namespace Kernel
17
18namespace Core {
19class System;
20} // namespace Core
21
22namespace Service::NFP {
23class NfpDevice;
24}
25
26namespace Service::AM::Applets {
27
28enum class CabinetAppletVersion : u32 {
29 Version1 = 0x1,
30};
31
32enum class CabinetResult : u8 {
33 Cancel = 0,
34 TagInfo = 1 << 1,
35 RegisterInfo = 1 << 2,
36 All = TagInfo | RegisterInfo,
37};
38DECLARE_ENUM_FLAG_OPERATORS(CabinetResult)
39
40// This is nn::nfp::AmiiboSettingsStartParam
41struct AmiiboSettingsStartParam {
42 u64 device_handle;
43 std::array<u8, 0x20> param_1;
44 u8 param_2;
45};
46static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
47 "AmiiboSettingsStartParam is an invalid size");
48
49#pragma pack(push, 1)
50// This is nn::nfp::StartParamForAmiiboSettings
51struct StartParamForAmiiboSettings {
52 u8 param_1;
53 Service::NFP::CabinetMode applet_mode;
54 u8 flags;
55 u8 amiibo_settings_1;
56 u64 device_handle;
57 Service::NFP::TagInfo tag_info;
58 Service::NFP::RegisterInfo register_info;
59 std::array<u8, 0x20> amiibo_settings_3;
60 INSERT_PADDING_BYTES(0x24);
61};
62static_assert(sizeof(StartParamForAmiiboSettings) == 0x1A8,
63 "StartParamForAmiiboSettings is an invalid size");
64
65// This is nn::nfp::ReturnValueForAmiiboSettings
66struct ReturnValueForAmiiboSettings {
67 CabinetResult result;
68 INSERT_PADDING_BYTES(0x3);
69 u64 device_handle;
70 Service::NFP::TagInfo tag_info;
71 Service::NFP::RegisterInfo register_info;
72 INSERT_PADDING_BYTES(0x24);
73};
74static_assert(sizeof(ReturnValueForAmiiboSettings) == 0x188,
75 "ReturnValueForAmiiboSettings is an invalid size");
76#pragma pack(pop)
77
78class Cabinet final : public Applet {
79public:
80 explicit Cabinet(Core::System& system_, LibraryAppletMode applet_mode_,
81 const Core::Frontend::CabinetApplet& frontend_);
82 ~Cabinet() override;
83
84 void Initialize() override;
85
86 bool TransactionComplete() const override;
87 Result GetStatus() const override;
88 void ExecuteInteractive() override;
89 void Execute() override;
90 void DisplayCompleted(bool apply_changes, std::string_view amiibo_name);
91 void Cancel();
92
93private:
94 const Core::Frontend::CabinetApplet& frontend;
95 Core::System& system;
96
97 bool is_complete{false};
98 std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
99 Kernel::KEvent* availability_change_event;
100 KernelHelpers::ServiceContext service_context;
101 StartParamForAmiiboSettings applet_input_common{};
102};
103
104} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index fcf34bf7e..bae0d99a6 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -144,6 +144,7 @@ void Error::Initialize() {
144 break; 144 break;
145 default: 145 default:
146 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); 146 UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode);
147 break;
147 } 148 }
148} 149}
149 150
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index c34ef08b3..e50acdaf6 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -129,6 +129,7 @@ void Auth::Execute() {
129 } 129 }
130 default: 130 default:
131 unimplemented_log(); 131 unimplemented_log();
132 break;
132 } 133 }
133} 134}
134 135
@@ -192,6 +193,7 @@ void PhotoViewer::Execute() {
192 break; 193 break;
193 default: 194 default:
194 UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); 195 UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode);
196 break;
195 } 197 }
196} 198}
197 199
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 7062df21c..10afbc2da 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/assert.h" 6#include "common/assert.h"
7#include "core/core.h" 7#include "core/core.h"
8#include "core/frontend/applets/cabinet.h"
8#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
9#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
10#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
@@ -16,6 +17,7 @@
16#include "core/hle/service/am/am.h" 17#include "core/hle/service/am/am.h"
17#include "core/hle/service/am/applet_ae.h" 18#include "core/hle/service/am/applet_ae.h"
18#include "core/hle/service/am/applet_oe.h" 19#include "core/hle/service/am/applet_oe.h"
20#include "core/hle/service/am/applets/applet_cabinet.h"
19#include "core/hle/service/am/applets/applet_controller.h" 21#include "core/hle/service/am/applets/applet_controller.h"
20#include "core/hle/service/am/applets/applet_error.h" 22#include "core/hle/service/am/applets/applet_error.h"
21#include "core/hle/service/am/applets/applet_general_backend.h" 23#include "core/hle/service/am/applets/applet_general_backend.h"
@@ -171,13 +173,15 @@ void Applet::Initialize() {
171 173
172AppletFrontendSet::AppletFrontendSet() = default; 174AppletFrontendSet::AppletFrontendSet() = default;
173 175
174AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 176AppletFrontendSet::AppletFrontendSet(CabinetApplet cabinet_applet,
177 ControllerApplet controller_applet, ErrorApplet error_applet,
175 MiiEdit mii_edit_, 178 MiiEdit mii_edit_,
176 ParentalControlsApplet parental_controls_applet, 179 ParentalControlsApplet parental_controls_applet,
177 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 180 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
178 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) 181 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
179 : controller{std::move(controller_applet)}, error{std::move(error_applet)}, 182 : cabinet{std::move(cabinet_applet)}, controller{std::move(controller_applet)},
180 mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)}, 183 error{std::move(error_applet)}, mii_edit{std::move(mii_edit_)},
184 parental_controls{std::move(parental_controls_applet)},
181 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, 185 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
182 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} 186 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
183 187
@@ -196,6 +200,10 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
196} 200}
197 201
198void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 202void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
203 if (set.cabinet != nullptr) {
204 frontend.cabinet = std::move(set.cabinet);
205 }
206
199 if (set.controller != nullptr) { 207 if (set.controller != nullptr) {
200 frontend.controller = std::move(set.controller); 208 frontend.controller = std::move(set.controller);
201 } 209 }
@@ -235,6 +243,10 @@ void AppletManager::SetDefaultAppletFrontendSet() {
235} 243}
236 244
237void AppletManager::SetDefaultAppletsIfMissing() { 245void AppletManager::SetDefaultAppletsIfMissing() {
246 if (frontend.cabinet == nullptr) {
247 frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
248 }
249
238 if (frontend.controller == nullptr) { 250 if (frontend.controller == nullptr) {
239 frontend.controller = 251 frontend.controller =
240 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore()); 252 std::make_unique<Core::Frontend::DefaultControllerApplet>(system.HIDCore());
@@ -279,6 +291,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
279 switch (id) { 291 switch (id) {
280 case AppletId::Auth: 292 case AppletId::Auth:
281 return std::make_shared<Auth>(system, mode, *frontend.parental_controls); 293 return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
294 case AppletId::Cabinet:
295 return std::make_shared<Cabinet>(system, mode, *frontend.cabinet);
282 case AppletId::Controller: 296 case AppletId::Controller:
283 return std::make_shared<Controller>(system, mode, *frontend.controller); 297 return std::make_shared<Controller>(system, mode, *frontend.controller);
284 case AppletId::Error: 298 case AppletId::Error:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 12c6a5b1a..a22eb62a8 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -16,6 +16,7 @@ class System;
16} 16}
17 17
18namespace Core::Frontend { 18namespace Core::Frontend {
19class CabinetApplet;
19class ControllerApplet; 20class ControllerApplet;
20class ECommerceApplet; 21class ECommerceApplet;
21class ErrorApplet; 22class ErrorApplet;
@@ -176,6 +177,7 @@ protected:
176}; 177};
177 178
178struct AppletFrontendSet { 179struct AppletFrontendSet {
180 using CabinetApplet = std::unique_ptr<Core::Frontend::CabinetApplet>;
179 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 181 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
180 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 182 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
181 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>; 183 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
@@ -186,10 +188,11 @@ struct AppletFrontendSet {
186 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 188 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
187 189
188 AppletFrontendSet(); 190 AppletFrontendSet();
189 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 191 AppletFrontendSet(CabinetApplet cabinet_applet, ControllerApplet controller_applet,
190 MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet, 192 ErrorApplet error_applet, MiiEdit mii_edit_,
191 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 193 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
192 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_); 194 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
195 WebBrowser web_browser_);
193 ~AppletFrontendSet(); 196 ~AppletFrontendSet();
194 197
195 AppletFrontendSet(const AppletFrontendSet&) = delete; 198 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -198,6 +201,7 @@ struct AppletFrontendSet {
198 AppletFrontendSet(AppletFrontendSet&&) noexcept; 201 AppletFrontendSet(AppletFrontendSet&&) noexcept;
199 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 202 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
200 203
204 CabinetApplet cabinet;
201 ControllerApplet controller; 205 ControllerApplet controller;
202 ErrorApplet error; 206 ErrorApplet error;
203 MiiEdit mii_edit; 207 MiiEdit mii_edit;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 79375bd2f..bf28440c6 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -36,8 +36,9 @@ namespace Service::HID {
36 36
37// Updating period for each HID device. 37// Updating period for each HID device.
38// Period time is obtained by measuring the number of samples in a second on HW using a homebrew 38// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
39// Correct pad_update_ns is 4ms this is overclocked to lower input lag 39// Correct npad_update_ns is 4ms this is overclocked to lower input lag
40constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) 40constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
41constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz)
41constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) 42constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
42constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) 43constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
43 44
@@ -75,11 +76,19 @@ IAppletResource::IAppletResource(Core::System& system_,
75 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); 76 GetController<Controller_Stubbed>(HidController::UniquePad).SetCommonHeaderOffset(0x5A00);
76 77
77 // Register update callbacks 78 // Register update callbacks
78 pad_update_event = Core::Timing::CreateEvent( 79 npad_update_event = Core::Timing::CreateEvent(
79 "HID::UpdatePadCallback", 80 "HID::UpdatePadCallback",
80 [this](std::uintptr_t user_data, s64 time, 81 [this](std::uintptr_t user_data, s64 time,
81 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { 82 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
82 const auto guard = LockService(); 83 const auto guard = LockService();
84 UpdateNpad(user_data, ns_late);
85 return std::nullopt;
86 });
87 default_update_event = Core::Timing::CreateEvent(
88 "HID::UpdateDefaultCallback",
89 [this](std::uintptr_t user_data, s64 time,
90 std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
91 const auto guard = LockService();
83 UpdateControllers(user_data, ns_late); 92 UpdateControllers(user_data, ns_late);
84 return std::nullopt; 93 return std::nullopt;
85 }); 94 });
@@ -100,7 +109,9 @@ IAppletResource::IAppletResource(Core::System& system_,
100 return std::nullopt; 109 return std::nullopt;
101 }); 110 });
102 111
103 system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event); 112 system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event);
113 system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns,
114 default_update_event);
104 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, 115 system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
105 mouse_keyboard_update_event); 116 mouse_keyboard_update_event);
106 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, 117 system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
@@ -118,7 +129,8 @@ void IAppletResource::DeactivateController(HidController controller) {
118} 129}
119 130
120IAppletResource::~IAppletResource() { 131IAppletResource::~IAppletResource() {
121 system.CoreTiming().UnscheduleEvent(pad_update_event, 0); 132 system.CoreTiming().UnscheduleEvent(npad_update_event, 0);
133 system.CoreTiming().UnscheduleEvent(default_update_event, 0);
122 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); 134 system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
123 system.CoreTiming().UnscheduleEvent(motion_update_event, 0); 135 system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
124} 136}
@@ -144,10 +156,20 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
144 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) { 156 if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
145 continue; 157 continue;
146 } 158 }
159 // Npad has it's own update event
160 if (controller == controllers[static_cast<size_t>(HidController::NPad)]) {
161 continue;
162 }
147 controller->OnUpdate(core_timing); 163 controller->OnUpdate(core_timing);
148 } 164 }
149} 165}
150 166
167void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
168 auto& core_timing = system.CoreTiming();
169
170 controllers[static_cast<size_t>(HidController::NPad)]->OnUpdate(core_timing);
171}
172
151void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, 173void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
152 std::chrono::nanoseconds ns_late) { 174 std::chrono::nanoseconds ns_late) {
153 auto& core_timing = system.CoreTiming(); 175 auto& core_timing = system.CoreTiming();
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 340d26fdc..b7c2a23ef 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -71,12 +71,14 @@ private:
71 71
72 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 72 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
73 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 73 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
74 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
74 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 75 void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
75 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 76 void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
76 77
77 KernelHelpers::ServiceContext& service_context; 78 KernelHelpers::ServiceContext& service_context;
78 79
79 std::shared_ptr<Core::Timing::EventType> pad_update_event; 80 std::shared_ptr<Core::Timing::EventType> npad_update_event;
81 std::shared_ptr<Core::Timing::EventType> default_update_event;
80 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; 82 std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
81 std::shared_ptr<Core::Timing::EventType> motion_update_event; 83 std::shared_ptr<Core::Timing::EventType> motion_update_event;
82 84
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
index 296603764..c860fd1a1 100644
--- a/src/core/hle/service/nfp/nfp_device.cpp
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -75,6 +75,9 @@ void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
75 LoadAmiibo(nfc_status.data); 75 LoadAmiibo(nfc_status.data);
76 break; 76 break;
77 case Common::Input::NfcState::AmiiboRemoved: 77 case Common::Input::NfcState::AmiiboRemoved:
78 if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
79 break;
80 }
78 if (device_state != DeviceState::SearchingForTag) { 81 if (device_state != DeviceState::SearchingForTag) {
79 CloseAmiibo(); 82 CloseAmiibo();
80 } 83 }
@@ -95,6 +98,8 @@ bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
95 return false; 98 return false;
96 } 99 }
97 100
101 // TODO: Filter by allowed_protocols here
102
98 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File)); 103 memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
99 104
100 device_state = DeviceState::TagFound; 105 device_state = DeviceState::TagFound;
@@ -141,7 +146,7 @@ void NfpDevice::Finalize() {
141 device_state = DeviceState::Unavailable; 146 device_state = DeviceState::Unavailable;
142} 147}
143 148
144Result NfpDevice::StartDetection(s32 protocol_) { 149Result NfpDevice::StartDetection(TagProtocol allowed_protocol) {
145 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) { 150 if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
146 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 151 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
147 return WrongDeviceState; 152 return WrongDeviceState;
@@ -153,7 +158,7 @@ Result NfpDevice::StartDetection(s32 protocol_) {
153 } 158 }
154 159
155 device_state = DeviceState::SearchingForTag; 160 device_state = DeviceState::SearchingForTag;
156 protocol = protocol_; 161 allowed_protocols = allowed_protocol;
157 return ResultSuccess; 162 return ResultSuccess;
158} 163}
159 164
@@ -467,6 +472,32 @@ Result NfpDevice::OpenApplicationArea(u32 access_id) {
467 return ResultSuccess; 472 return ResultSuccess;
468} 473}
469 474
475Result NfpDevice::GetApplicationAreaId(u32& application_area_id) const {
476 application_area_id = {};
477
478 if (device_state != DeviceState::TagMounted) {
479 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
480 if (device_state == DeviceState::TagRemoved) {
481 return TagRemoved;
482 }
483 return WrongDeviceState;
484 }
485
486 if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
487 LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
488 return WrongDeviceState;
489 }
490
491 if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
492 LOG_WARNING(Service_NFP, "Application area is not initialized");
493 return ApplicationAreaIsNotInitialized;
494 }
495
496 application_area_id = tag_data.application_area_id;
497
498 return ResultSuccess;
499}
500
470Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const { 501Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
471 if (device_state != DeviceState::TagMounted) { 502 if (device_state != DeviceState::TagMounted) {
472 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state); 503 LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
index 6d8fc1099..b6a46f2ac 100644
--- a/src/core/hle/service/nfp/nfp_device.h
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <span>
6#include <vector> 7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
@@ -36,7 +37,7 @@ public:
36 void Initialize(); 37 void Initialize();
37 void Finalize(); 38 void Finalize();
38 39
39 Result StartDetection(s32 protocol_); 40 Result StartDetection(TagProtocol allowed_protocol);
40 Result StopDetection(); 41 Result StopDetection();
41 Result Mount(MountTarget mount_target); 42 Result Mount(MountTarget mount_target);
42 Result Unmount(); 43 Result Unmount();
@@ -52,6 +53,7 @@ public:
52 Result DeleteAllData(); 53 Result DeleteAllData();
53 54
54 Result OpenApplicationArea(u32 access_id); 55 Result OpenApplicationArea(u32 access_id);
56 Result GetApplicationAreaId(u32& application_area_id) const;
55 Result GetApplicationArea(std::vector<u8>& data) const; 57 Result GetApplicationArea(std::vector<u8>& data) const;
56 Result SetApplicationArea(std::span<const u8> data); 58 Result SetApplicationArea(std::span<const u8> data);
57 Result CreateApplicationArea(u32 access_id, std::span<const u8> data); 59 Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
@@ -87,7 +89,7 @@ private:
87 89
88 bool is_data_moddified{}; 90 bool is_data_moddified{};
89 bool is_app_area_open{}; 91 bool is_app_area_open{};
90 s32 protocol{}; 92 TagProtocol allowed_protocols{};
91 s64 current_posix_time{}; 93 s64 current_posix_time{};
92 MountTarget mount_target{MountTarget::None}; 94 MountTarget mount_target{MountTarget::None};
93 DeviceState device_state{DeviceState::Unavailable}; 95 DeviceState device_state{DeviceState::Unavailable};
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
index 63d5917cb..69858096a 100644
--- a/src/core/hle/service/nfp/nfp_types.h
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -88,11 +88,22 @@ enum class PackedTagType : u8 {
88 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s 88 Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
89}; 89};
90 90
91// Verify this enum. It might be completely wrong default protocol is 0x48
91enum class TagProtocol : u32 { 92enum class TagProtocol : u32 {
92 None, 93 None,
93 TypeA, // ISO14443A 94 TypeA = 1U << 0, // ISO14443A
94 TypeB, // ISO14443B 95 TypeB = 1U << 1, // ISO14443B
95 TypeF, // Sony Felica 96 TypeF = 1U << 2, // Sony Felica
97 Unknown1 = 1U << 3,
98 Unknown2 = 1U << 5,
99 All = 0xFFFFFFFFU,
100};
101
102enum class CabinetMode : u8 {
103 StartNicknameAndOwnerSettings,
104 StartGameDataEraser,
105 StartRestorer,
106 StartFormatter,
96}; 107};
97 108
98using UniqueSerialNumber = std::array<u8, 7>; 109using UniqueSerialNumber = std::array<u8, 7>;
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 3f7f17598..2fe3c0ea0 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -131,7 +131,7 @@ void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
131void IUser::StartDetection(Kernel::HLERequestContext& ctx) { 131void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
132 IPC::RequestParser rp{ctx}; 132 IPC::RequestParser rp{ctx};
133 const auto device_handle{rp.Pop<u64>()}; 133 const auto device_handle{rp.Pop<u64>()};
134 const auto nfp_protocol{rp.Pop<s32>()}; 134 const auto nfp_protocol{rp.PopEnum<TagProtocol>()};
135 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol); 135 LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
136 136
137 if (state == State::NonInitialized) { 137 if (state == State::NonInitialized) {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index ced57dfe6..b97813fbc 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
300 return error_notifier_event; 300 return error_notifier_event;
301 case 2: 301 case 2:
302 return unknown_event; 302 return unknown_event;
303 default: { 303 default:
304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); 304 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
305 return nullptr;
305 } 306 }
306 }
307 return nullptr;
308} 307}
309 308
310} // namespace Service::Nvidia::Devices 309} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 45a759fa8..e123564c6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
364 return sm_exception_breakpoint_pause_report_event; 364 return sm_exception_breakpoint_pause_report_event;
365 case 3: 365 case 3:
366 return error_notifier_event; 366 return error_notifier_event;
367 default: { 367 default:
368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); 368 LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
369 return nullptr;
369 } 370 }
370 }
371 return nullptr;
372} 371}
373 372
374} // namespace Service::Nvidia::Devices 373} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
index ea4a14ea4..3d1338e66 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() {
23} 23}
24 24
25void BufferQueueCore::SignalDequeueCondition() { 25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true);
26 dequeue_condition.notify_all(); 27 dequeue_condition.notify_all();
27} 28}
28 29
29bool BufferQueueCore::WaitForDequeueCondition() { 30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
30 if (is_shutting_down) { 31 if (is_shutting_down) {
31 return false; 32 return false;
32 } 33 }
33 34
34 dequeue_condition.wait(mutex); 35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false);
35 37
36 return true; 38 return true;
37} 39}
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
index ca6baefaf..85b3bc4c1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -38,7 +38,7 @@ public:
38 38
39private: 39private:
40 void SignalDequeueCondition(); 40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(); 41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
42 42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const; 43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const; 44 s32 GetMinMaxBufferCountLocked(bool async) const;
@@ -60,7 +60,8 @@ private:
60 BufferQueueDefs::SlotsType slots{}; 60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue; 61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{}; 62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition; 63 std::condition_variable dequeue_condition;
64 std::atomic<bool> dequeue_possible{};
64 const bool use_async_buffer{}; // This is always disabled on HOS 65 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{}; 66 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888}; 67 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
index 41ba44b21..e601b5da1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
121 return Status::NoError; 121 return Status::NoError;
122} 122}
123 123
124Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, 124Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
125 Status* return_flags) const { 125 std::unique_lock<std::mutex>& lk) const {
126 bool try_again = true; 126 bool try_again = true;
127 127
128 while (try_again) { 128 while (try_again) {
@@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
214 return Status::WouldBlock; 214 return Status::WouldBlock;
215 } 215 }
216 216
217 if (!core->WaitForDequeueCondition()) { 217 if (!core->WaitForDequeueCondition(lk)) {
218 // We are no longer running 218 // We are no longer running
219 return Status::NoError; 219 return Status::NoError;
220 } 220 }
@@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
237 Status return_flags = Status::NoError; 237 Status return_flags = Status::NoError;
238 bool attached_by_consumer = false; 238 bool attached_by_consumer = false;
239 { 239 {
240 std::scoped_lock lock{core->mutex}; 240 std::unique_lock lock{core->mutex};
241 core->WaitWhileAllocatingLocked(); 241 core->WaitWhileAllocatingLocked();
242 242
243 if (format == PixelFormat::NoFormat) { 243 if (format == PixelFormat::NoFormat) {
@@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
248 usage |= core->consumer_usage_bit; 248 usage |= core->consumer_usage_bit;
249 249
250 s32 found{}; 250 s32 found{};
251 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); 251 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock);
252 if (status != Status::NoError) { 252 if (status != Status::NoError) {
253 return status; 253 return status;
254 } 254 }
@@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
400 return Status::BadValue; 400 return Status::BadValue;
401 } 401 }
402 402
403 std::scoped_lock lock{core->mutex}; 403 std::unique_lock lock{core->mutex};
404 core->WaitWhileAllocatingLocked(); 404 core->WaitWhileAllocatingLocked();
405 405
406 Status return_flags = Status::NoError; 406 Status return_flags = Status::NoError;
407 s32 found{}; 407 s32 found{};
408 408
409 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); 409 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock);
410 if (status != Status::NoError) { 410 if (status != Status::NoError) {
411 return status; 411 return status;
412 } 412 }
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
index 7526bf8ec..1d380480f 100644
--- a/src/core/hle/service/nvflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -70,7 +70,8 @@ public:
70private: 70private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 71 BufferQueueProducer(const BufferQueueProducer&) = delete;
72 72
73 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; 73 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags,
74 std::unique_lock<std::mutex>& lk) const;
74 75
75 Kernel::KEvent* buffer_wait_event{}; 76 Kernel::KEvent* buffer_wait_event{};
76 Service::KernelHelpers::ServiceContext& service_context; 77 Service::KernelHelpers::ServiceContext& service_context;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 5ab41c0c4..0de67f1e1 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
228 } 228 }
229 229
230 UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); 230 UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType());
231 break;
231 } 232 }
232 233
233 // If emulation was shutdown, we are closing service threads, do not write the response back to 234 // If emulation was shutdown, we are closing service threads, do not write the response back to
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 2aa675df9..f9ada7c93 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
280 } 280 }
281 default: 281 default:
282 ASSERT(false); 282 ASSERT(false);
283 break;
283 } 284 }
284 return value + rule.transition_time + offset; 285 return value + rule.transition_time + offset;
285} 286}
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 3ca80c8ff..3141122f1 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/atomic_ops.h" 8#include "common/atomic_ops.h"
9#include "common/cache_management.h"
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "common/page_table.h" 12#include "common/page_table.h"
@@ -329,6 +330,55 @@ struct Memory::Impl {
329 }); 330 });
330 } 331 }
331 332
333 template <typename Callback>
334 Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size,
335 Callback&& cb) {
336 class InvalidMemoryException : public std::exception {};
337
338 try {
339 WalkBlock(
340 process, dest_addr, size,
341 [&](const std::size_t block_size, const VAddr current_vaddr) {
342 LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr);
343 throw InvalidMemoryException();
344 },
345 [&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); },
346 [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) {
347 system.GPU().FlushRegion(current_vaddr, block_size);
348 cb(block_size, host_ptr);
349 },
350 [](const std::size_t block_size) {});
351 } catch (InvalidMemoryException&) {
352 return Kernel::ResultInvalidCurrentMemory;
353 }
354
355 return ResultSuccess;
356 }
357
358 Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
359 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
360 // Do nothing; this operation (dc ivac) cannot be supported
361 // from EL0
362 };
363 return PerformCacheOperation(process, dest_addr, size, perform);
364 }
365
366 Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
367 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
368 // dc cvac: Store to point of coherency
369 Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size);
370 };
371 return PerformCacheOperation(process, dest_addr, size, perform);
372 }
373
374 Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) {
375 auto perform = [&](const std::size_t block_size, u8* const host_ptr) {
376 // dc civac: Store to point of coherency, and invalidate from cache
377 Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size);
378 };
379 return PerformCacheOperation(process, dest_addr, size, perform);
380 }
381
332 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { 382 void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
333 if (vaddr == 0) { 383 if (vaddr == 0) {
334 return; 384 return;
@@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s
786 impl->ZeroBlock(process, dest_addr, size); 836 impl->ZeroBlock(process, dest_addr, size);
787} 837}
788 838
839Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr,
840 const std::size_t size) {
841 return impl->InvalidateDataCache(process, dest_addr, size);
842}
843
844Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr,
845 const std::size_t size) {
846 return impl->StoreDataCache(process, dest_addr, size);
847}
848
849Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr,
850 const std::size_t size) {
851 return impl->FlushDataCache(process, dest_addr, size);
852}
853
789void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { 854void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
790 impl->RasterizerMarkRegionCached(vaddr, size, cached); 855 impl->RasterizerMarkRegionCached(vaddr, size, cached);
791} 856}
diff --git a/src/core/memory.h b/src/core/memory.h
index 81eac448b..31fe699d8 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/result.h"
10 11
11namespace Common { 12namespace Common {
12struct PageTable; 13struct PageTable;
@@ -450,6 +451,39 @@ public:
450 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); 451 void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
451 452
452 /** 453 /**
454 * Invalidates a range of bytes within the current process' address space at the specified
455 * virtual address.
456 *
457 * @param process The process that will have data invalidated within its address space.
458 * @param dest_addr The destination virtual address to invalidate the data from.
459 * @param size The size of the range to invalidate, in bytes.
460 *
461 */
462 Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
463
464 /**
465 * Stores a range of bytes within the current process' address space at the specified
466 * virtual address.
467 *
468 * @param process The process that will have data stored within its address space.
469 * @param dest_addr The destination virtual address to store the data from.
470 * @param size The size of the range to store, in bytes.
471 *
472 */
473 Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
474
475 /**
476 * Flushes a range of bytes within the current process' address space at the specified
477 * virtual address.
478 *
479 * @param process The process that will have data flushed within its address space.
480 * @param dest_addr The destination virtual address to flush the data from.
481 * @param size The size of the range to flush, in bytes.
482 *
483 */
484 Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
485
486 /**
453 * Marks each page within the specified address range as cached or uncached. 487 * Marks each page within the specified address range as cached or uncached.
454 * 488 *
455 * @param vaddr The virtual address indicating the start of the address range. 489 * @param vaddr The virtual address indicating the start of the address range.
diff --git a/src/input_common/drivers/virtual_amiibo.cpp b/src/input_common/drivers/virtual_amiibo.cpp
index 0cd5129da..564a188e5 100644
--- a/src/input_common/drivers/virtual_amiibo.cpp
+++ b/src/input_common/drivers/virtual_amiibo.cpp
@@ -60,6 +60,8 @@ Common::Input::NfcState VirtualAmiibo::WriteNfcData(
60 return Common::Input::NfcState::WriteFailed; 60 return Common::Input::NfcState::WriteFailed;
61 } 61 }
62 62
63 amiibo_data = data;
64
63 return Common::Input::NfcState::Success; 65 return Common::Input::NfcState::Success;
64} 66}
65 67
@@ -91,6 +93,15 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(const std::string& filename) {
91 return Info::Success; 93 return Info::Success;
92} 94}
93 95
96VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
97 if (state == State::AmiiboIsOpen) {
98 SetNfc(identifier, {Common::Input::NfcState::NewAmiibo, amiibo_data});
99 return Info::Success;
100 }
101
102 return LoadAmiibo(file_path);
103}
104
94VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() { 105VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
95 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo 106 state = polling_mode == Common::Input::PollingMode::NFC ? State::WaitingForAmiibo
96 : State::Initialized; 107 : State::Initialized;
@@ -98,4 +109,8 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
98 return Info::Success; 109 return Info::Success;
99} 110}
100 111
112std::string VirtualAmiibo::GetLastFilePath() const {
113 return file_path;
114}
115
101} // namespace InputCommon 116} // namespace InputCommon
diff --git a/src/input_common/drivers/virtual_amiibo.h b/src/input_common/drivers/virtual_amiibo.h
index 9eac07544..9baeb3997 100644
--- a/src/input_common/drivers/virtual_amiibo.h
+++ b/src/input_common/drivers/virtual_amiibo.h
@@ -47,8 +47,11 @@ public:
47 State GetCurrentState() const; 47 State GetCurrentState() const;
48 48
49 Info LoadAmiibo(const std::string& amiibo_file); 49 Info LoadAmiibo(const std::string& amiibo_file);
50 Info ReloadAmiibo();
50 Info CloseAmiibo(); 51 Info CloseAmiibo();
51 52
53 std::string GetLastFilePath() const;
54
52private: 55private:
53 static constexpr std::size_t amiibo_size = 0x21C; 56 static constexpr std::size_t amiibo_size = 0x21C;
54 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8; 57 static constexpr std::size_t amiibo_size_without_password = amiibo_size - 0x8;
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index da4a3dca5..003a38da5 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -10,8 +10,8 @@ namespace InputCommon {
10class TouchFromButtonDevice final : public Common::Input::InputDevice { 10class TouchFromButtonDevice final : public Common::Input::InputDevice {
11public: 11public:
12 using Button = std::unique_ptr<Common::Input::InputDevice>; 12 using Button = std::unique_ptr<Common::Input::InputDevice>;
13 TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_) 13 TouchFromButtonDevice(Button button_, float x_, float y_)
14 : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) { 14 : button(std::move(button_)), x(x_), y(y_) {
15 last_button_value = false; 15 last_button_value = false;
16 button->SetCallback({ 16 button->SetCallback({
17 .on_change = 17 .on_change =
@@ -34,7 +34,6 @@ public:
34 .pressed = button_status, 34 .pressed = button_status,
35 .x = {}, 35 .x = {},
36 .y = {}, 36 .y = {},
37 .id = touch_id,
38 }; 37 };
39 status.x.properties = properties; 38 status.x.properties = properties;
40 status.y.properties = properties; 39 status.y.properties = properties;
@@ -62,7 +61,6 @@ public:
62private: 61private:
63 Button button; 62 Button button;
64 bool last_button_value; 63 bool last_button_value;
65 const int touch_id;
66 const float x; 64 const float x;
67 const float y; 65 const float y;
68 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false}; 66 const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
@@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
73 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize(); 71 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
74 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>( 72 auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
75 params.Get("button", null_engine)); 73 params.Get("button", null_engine));
76 const auto touch_id = params.Get("touch_id", 0);
77 const float x = params.Get("x", 0.0f) / 1280.0f; 74 const float x = params.Get("x", 0.0f) / 1280.0f;
78 const float y = params.Get("y", 0.0f) / 720.0f; 75 const float y = params.Get("y", 0.0f) / 720.0f;
79 return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y); 76 return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
80} 77}
81 78
82} // namespace InputCommon 79} // namespace InputCommon
diff --git a/src/input_common/input_engine.h b/src/input_common/input_engine.h
index d4c264a8e..6cbcf5207 100644
--- a/src/input_common/input_engine.h
+++ b/src/input_common/input_engine.h
@@ -133,7 +133,7 @@ public:
133 return Common::Input::CameraError::NotSupported; 133 return Common::Input::CameraError::NotSupported;
134 } 134 }
135 135
136 // Request nfc data from a controller 136 // Returns success if nfc is supported
137 virtual Common::Input::NfcState SupportsNfc( 137 virtual Common::Input::NfcState SupportsNfc(
138 [[maybe_unused]] const PadIdentifier& identifier) const { 138 [[maybe_unused]] const PadIdentifier& identifier) const {
139 return Common::Input::NfcState::NotSupported; 139 return Common::Input::NfcState::NotSupported;
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 4ac182147..fb8be42e2 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -229,13 +229,12 @@ private:
229 229
230class InputFromTouch final : public Common::Input::InputDevice { 230class InputFromTouch final : public Common::Input::InputDevice {
231public: 231public:
232 explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_, 232 explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
233 bool inverted_, int axis_x_, int axis_y_, 233 int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
234 Common::Input::AnalogProperties properties_x_,
235 Common::Input::AnalogProperties properties_y_, 234 Common::Input::AnalogProperties properties_y_,
236 InputEngine* input_engine_) 235 InputEngine* input_engine_)
237 : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_), 236 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
238 inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_), 237 axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
239 properties_y(properties_y_), input_engine(input_engine_) { 238 properties_y(properties_y_), input_engine(input_engine_) {
240 UpdateCallback engine_callback{[this]() { OnChange(); }}; 239 UpdateCallback engine_callback{[this]() { OnChange(); }};
241 const InputIdentifier button_input_identifier{ 240 const InputIdentifier button_input_identifier{
@@ -271,8 +270,7 @@ public:
271 } 270 }
272 271
273 Common::Input::TouchStatus GetStatus() const { 272 Common::Input::TouchStatus GetStatus() const {
274 Common::Input::TouchStatus status; 273 Common::Input::TouchStatus status{};
275 status.id = touch_id;
276 status.pressed = { 274 status.pressed = {
277 .value = input_engine->GetButton(identifier, button), 275 .value = input_engine->GetButton(identifier, button),
278 .inverted = inverted, 276 .inverted = inverted,
@@ -307,7 +305,6 @@ public:
307 305
308private: 306private:
309 const PadIdentifier identifier; 307 const PadIdentifier identifier;
310 const int touch_id;
311 const int button; 308 const int button;
312 const bool toggle; 309 const bool toggle;
313 const bool inverted; 310 const bool inverted;
@@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
919 916
920std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice( 917std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
921 const Common::ParamPackage& params) { 918 const Common::ParamPackage& params) {
922 const auto touch_id = params.Get("touch_id", 0);
923 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f); 919 const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
924 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f); 920 const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
925 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f); 921 const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
@@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
954 input_engine->PreSetAxis(identifier, axis_x); 950 input_engine->PreSetAxis(identifier, axis_x);
955 input_engine->PreSetAxis(identifier, axis_y); 951 input_engine->PreSetAxis(identifier, axis_y);
956 input_engine->PreSetButton(identifier, button); 952 input_engine->PreSetButton(identifier, button);
957 return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x, 953 return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
958 axis_y, properties_x, properties_y, input_engine.get()); 954 properties_x, properties_y, input_engine.get());
959} 955}
960 956
961std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice( 957std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 3b0176bf6..0cb1e193e 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -320,6 +320,7 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
320 } 320 }
321 if (stage == Stage::Fragment) { 321 if (stage == Stage::Fragment) {
322 header += "OPTION ARB_draw_buffers;"; 322 header += "OPTION ARB_draw_buffers;";
323 header += "OPTION ARB_fragment_layer_viewport;";
323 } 324 }
324} 325}
325 326
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index d6562c842..f0bd84ab2 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -104,6 +104,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, Scal
104 case IR::Attribute::PrimitiveId: 104 case IR::Attribute::PrimitiveId:
105 ctx.Add("MOV.F {}.x,primitive.id;", inst); 105 ctx.Add("MOV.F {}.x,primitive.id;", inst);
106 break; 106 break;
107 case IR::Attribute::Layer:
108 ctx.Add("MOV.F {}.x,fragment.layer;", inst);
109 break;
107 case IR::Attribute::PositionX: 110 case IR::Attribute::PositionX:
108 case IR::Attribute::PositionY: 111 case IR::Attribute::PositionY:
109 case IR::Attribute::PositionZ: 112 case IR::Attribute::PositionZ:
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index c1671c37b..39579cf5d 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -205,6 +205,9 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
205 case IR::Attribute::PrimitiveId: 205 case IR::Attribute::PrimitiveId:
206 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst); 206 ctx.AddF32("{}=itof(gl_PrimitiveID);", inst);
207 break; 207 break;
208 case IR::Attribute::Layer:
209 ctx.AddF32("{}=itof(gl_Layer);", inst);
210 break;
208 case IR::Attribute::PositionX: 211 case IR::Attribute::PositionX:
209 case IR::Attribute::PositionY: 212 case IR::Attribute::PositionY:
210 case IR::Attribute::PositionZ: 213 case IR::Attribute::PositionZ:
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 5b3b5d1f3..01f6ec9b5 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -315,6 +315,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
315 switch (attr) { 315 switch (attr) {
316 case IR::Attribute::PrimitiveId: 316 case IR::Attribute::PrimitiveId:
317 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id)); 317 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
318 case IR::Attribute::Layer:
319 return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.layer));
318 case IR::Attribute::PositionX: 320 case IR::Attribute::PositionX:
319 case IR::Attribute::PositionY: 321 case IR::Attribute::PositionY:
320 case IR::Attribute::PositionZ: 322 case IR::Attribute::PositionZ:
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 0bfc2dd89..8e3e40cd5 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1359,6 +1359,11 @@ void EmitContext::DefineInputs(const IR::Program& program) {
1359 if (loads[IR::Attribute::PrimitiveId]) { 1359 if (loads[IR::Attribute::PrimitiveId]) {
1360 primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); 1360 primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
1361 } 1361 }
1362 if (loads[IR::Attribute::Layer]) {
1363 AddCapability(spv::Capability::Geometry);
1364 layer = DefineInput(*this, U32[1], false, spv::BuiltIn::Layer);
1365 Decorate(layer, spv::Decoration::Flat);
1366 }
1362 if (loads.AnyComponent(IR::Attribute::PositionX)) { 1367 if (loads.AnyComponent(IR::Attribute::PositionX)) {
1363 const bool is_fragment{stage != Stage::Fragment}; 1368 const bool is_fragment{stage != Stage::Fragment};
1364 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 1369 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index d502d181c..5bb1427c1 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -232,7 +232,7 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
232 use_topology_override = true; 232 use_topology_override = true;
233 return; 233 return;
234 case MAXWELL3D_REG_INDEX(clear_surface): 234 case MAXWELL3D_REG_INDEX(clear_surface):
235 return ProcessClearBuffers(); 235 return ProcessClearBuffers(1);
236 case MAXWELL3D_REG_INDEX(report_semaphore.query): 236 case MAXWELL3D_REG_INDEX(report_semaphore.query):
237 return ProcessQueryGet(); 237 return ProcessQueryGet();
238 case MAXWELL3D_REG_INDEX(render_enable.mode): 238 case MAXWELL3D_REG_INDEX(render_enable.mode):
@@ -596,8 +596,8 @@ u32 Maxwell3D::GetRegisterValue(u32 method) const {
596 return regs.reg_array[method]; 596 return regs.reg_array[method];
597} 597}
598 598
599void Maxwell3D::ProcessClearBuffers() { 599void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
600 rasterizer->Clear(); 600 rasterizer->Clear(layer_count);
601} 601}
602 602
603void Maxwell3D::ProcessDraw(u32 instance_count) { 603void Maxwell3D::ProcessDraw(u32 instance_count) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 34b085388..c3099f9a6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -3086,6 +3086,9 @@ public:
3086 3086
3087 std::vector<u8> inline_index_draw_indexes; 3087 std::vector<u8> inline_index_draw_indexes;
3088 3088
3089 /// Handles a write to the CLEAR_BUFFERS register.
3090 void ProcessClearBuffers(u32 layer_count);
3091
3089private: 3092private:
3090 void InitializeRegisterDefaults(); 3093 void InitializeRegisterDefaults();
3091 3094
@@ -3120,9 +3123,6 @@ private:
3120 /// Handles firmware blob 4 3123 /// Handles firmware blob 4
3121 void ProcessFirmwareCall4(); 3124 void ProcessFirmwareCall4();
3122 3125
3123 /// Handles a write to the CLEAR_BUFFERS register.
3124 void ProcessClearBuffers();
3125
3126 /// Handles a write to the QUERY_GET register. 3126 /// Handles a write to the QUERY_GET register.
3127 void ProcessQueryGet(); 3127 void ProcessQueryGet();
3128 3128
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index 54523a4b2..1bf6ca2dd 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -314,6 +314,7 @@ void MaxwellDMA::ReleaseSemaphore() {
314 } 314 }
315 default: 315 default:
316 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value())); 316 ASSERT_MSG(false, "Unknown semaphore type: {}", static_cast<u32>(type.Value()));
317 break;
317 } 318 }
318} 319}
319 320
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp
index 3977bb0fb..4d2278811 100644
--- a/src/video_core/engines/puller.cpp
+++ b/src/video_core/engines/puller.cpp
@@ -50,6 +50,7 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) {
50 break; 50 break;
51 default: 51 default:
52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); 52 UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id);
53 break;
53 } 54 }
54} 55}
55 56
@@ -65,6 +66,7 @@ void Puller::ProcessFenceActionMethod() {
65 break; 66 break;
66 default: 67 default:
67 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value()); 68 UNIMPLEMENTED_MSG("Unimplemented operation {}", regs.fence_action.op.Value());
69 break;
68 } 70 }
69} 71}
70 72
@@ -228,6 +230,7 @@ void Puller::CallEngineMethod(const MethodCall& method_call) {
228 break; 230 break;
229 default: 231 default:
230 UNIMPLEMENTED_MSG("Unimplemented engine"); 232 UNIMPLEMENTED_MSG("Unimplemented engine");
233 break;
231 } 234 }
232} 235}
233 236
@@ -254,6 +257,7 @@ void Puller::CallEngineMultiMethod(u32 method, u32 subchannel, const u32* base_s
254 break; 257 break;
255 default: 258 default:
256 UNIMPLEMENTED_MSG("Unimplemented engine"); 259 UNIMPLEMENTED_MSG("Unimplemented engine");
260 break;
257 } 261 }
258} 262}
259 263
diff --git a/src/video_core/macro/macro_hle.cpp b/src/video_core/macro/macro_hle.cpp
index f896591bf..0f3262edb 100644
--- a/src/video_core/macro/macro_hle.cpp
+++ b/src/video_core/macro/macro_hle.cpp
@@ -126,11 +126,25 @@ void HLE_3F5E74B9C9A50164(Engines::Maxwell3D& maxwell3d, const std::vector<u32>&
126 } 126 }
127} 127}
128 128
129constexpr std::array<std::pair<u64, HLEFunction>, 4> hle_funcs{{ 129// Multi-layer Clear
130void HLE_EAD26C3E2109B06B(Engines::Maxwell3D& maxwell3d, const std::vector<u32>& parameters) {
131 ASSERT(parameters.size() == 1);
132
133 const Engines::Maxwell3D::Regs::ClearSurface clear_params{parameters[0]};
134 const u32 rt_index = clear_params.RT;
135 const u32 num_layers = maxwell3d.regs.rt[rt_index].depth;
136 ASSERT(clear_params.layer == 0);
137
138 maxwell3d.regs.clear_surface.raw = clear_params.raw;
139 maxwell3d.ProcessClearBuffers(num_layers);
140}
141
142constexpr std::array<std::pair<u64, HLEFunction>, 5> hle_funcs{{
130 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0}, 143 {0x771BB18C62444DA0, &HLE_771BB18C62444DA0},
131 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD}, 144 {0x0D61FC9FAAC9FCAD, &HLE_0D61FC9FAAC9FCAD},
132 {0x0217920100488FF7, &HLE_0217920100488FF7}, 145 {0x0217920100488FF7, &HLE_0217920100488FF7},
133 {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164}, 146 {0x3F5E74B9C9A50164, &HLE_3F5E74B9C9A50164},
147 {0xEAD26C3E2109B06B, &HLE_EAD26C3E2109B06B},
134}}; 148}};
135 149
136class HLEMacroImpl final : public CachedMacro { 150class HLEMacroImpl final : public CachedMacro {
diff --git a/src/video_core/macro/macro_interpreter.cpp b/src/video_core/macro/macro_interpreter.cpp
index c0d32c112..0d63495a9 100644
--- a/src/video_core/macro/macro_interpreter.cpp
+++ b/src/video_core/macro/macro_interpreter.cpp
@@ -201,6 +201,7 @@ bool MacroInterpreterImpl::Step(bool is_delay_slot) {
201 } 201 }
202 default: 202 default:
203 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value()); 203 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", opcode.operation.Value());
204 break;
204 } 205 }
205 206
206 // An instruction with the Exit flag will not actually 207 // An instruction with the Exit flag will not actually
@@ -297,6 +298,7 @@ void MacroInterpreterImpl::ProcessResult(Macro::ResultOperation operation, u32 r
297 break; 298 break;
298 default: 299 default:
299 UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation); 300 UNIMPLEMENTED_MSG("Unimplemented result operation {}", operation);
301 break;
300 } 302 }
301} 303}
302 304
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index 25c1ce798..7347cbd88 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -652,6 +652,7 @@ void MacroJITx64Impl::Compile_ProcessResult(Macro::ResultOperation operation, u3
652 break; 652 break;
653 default: 653 default:
654 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation); 654 UNIMPLEMENTED_MSG("Unimplemented macro operation {}", operation);
655 break;
655 } 656 }
656} 657}
657 658
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 1cbfef090..cfd872a40 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -43,7 +43,7 @@ public:
43 virtual void Draw(bool is_indexed, u32 instance_count) = 0; 43 virtual void Draw(bool is_indexed, u32 instance_count) = 0;
44 44
45 /// Clear the current framebuffer 45 /// Clear the current framebuffer
46 virtual void Clear() = 0; 46 virtual void Clear(u32 layer_count) = 0;
47 47
48 /// Dispatches a compute shader invocation 48 /// Dispatches a compute shader invocation
49 virtual void DispatchCompute() = 0; 49 virtual void DispatchCompute() = 0;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d05a5f60b..115a5e010 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -136,7 +136,7 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
136 shader_cache.LoadDiskResources(title_id, stop_loading, callback); 136 shader_cache.LoadDiskResources(title_id, stop_loading, callback);
137} 137}
138 138
139void RasterizerOpenGL::Clear() { 139void RasterizerOpenGL::Clear(u32 layer_count) {
140 MICROPROFILE_SCOPE(OpenGL_Clears); 140 MICROPROFILE_SCOPE(OpenGL_Clears);
141 if (!maxwell3d->ShouldExecute()) { 141 if (!maxwell3d->ShouldExecute()) {
142 return; 142 return;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 793e0d608..449a14f12 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -69,7 +69,7 @@ public:
69 ~RasterizerOpenGL() override; 69 ~RasterizerOpenGL() override;
70 70
71 void Draw(bool is_indexed, u32 instance_count) override; 71 void Draw(bool is_indexed, u32 instance_count) override;
72 void Clear() override; 72 void Clear(u32 layer_count) override;
73 void DispatchCompute() override; 73 void DispatchCompute() override;
74 void ResetCounter(VideoCore::QueryType type) override; 74 void ResetCounter(VideoCore::QueryType type) override;
75 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; 75 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 99cd11d1e..9f7ce7414 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -891,6 +891,7 @@ void Image::CopyBufferToImage(const VideoCommon::BufferImageCopy& copy, size_t b
891 break; 891 break;
892 default: 892 default:
893 ASSERT(false); 893 ASSERT(false);
894 break;
894 } 895 }
895} 896}
896 897
@@ -927,6 +928,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
927 break; 928 break;
928 default: 929 default:
929 ASSERT(false); 930 ASSERT(false);
931 break;
930 } 932 }
931 // Compressed formats don't have a pixel format or type 933 // Compressed formats don't have a pixel format or type
932 const bool is_compressed = gl_format == GL_NONE; 934 const bool is_compressed = gl_format == GL_NONE;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8bd5eba7e..f29462f7c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -340,6 +340,7 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
340 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; 340 texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
341 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", 341 // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}",
342 // static_cast<u32>(framebuffer.pixel_format)); 342 // static_cast<u32>(framebuffer.pixel_format));
343 break;
343 } 344 }
344 345
345 texture.resource.Release(); 346 texture.resource.Release();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index d8131232a..c2a95200b 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -172,6 +172,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
172} 172}
173 173
174void RendererVulkan::Report() const { 174void RendererVulkan::Report() const {
175 using namespace Common::Literals;
175 const std::string vendor_name{device.GetVendorName()}; 176 const std::string vendor_name{device.GetVendorName()};
176 const std::string model_name{device.GetModelName()}; 177 const std::string model_name{device.GetModelName()};
177 const std::string driver_version = GetDriverVersion(device); 178 const std::string driver_version = GetDriverVersion(device);
@@ -181,9 +182,12 @@ void RendererVulkan::Report() const {
181 182
182 const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions()); 183 const std::string extensions = BuildCommaSeparatedExtensions(device.GetAvailableExtensions());
183 184
185 const auto available_vram = static_cast<f64>(device.GetDeviceLocalMemory()) / f64{1_GiB};
186
184 LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); 187 LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
185 LOG_INFO(Render_Vulkan, "Device: {}", model_name); 188 LOG_INFO(Render_Vulkan, "Device: {}", model_name);
186 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); 189 LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
190 LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram);
187 191
188 static constexpr auto field = Common::Telemetry::FieldType::UserSystem; 192 static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
189 telemetry_session.AddField(field, "GPU_Vendor", vendor_name); 193 telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index f69c0c50f..67b88621a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -213,7 +213,7 @@ void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
213 EndTransformFeedback(); 213 EndTransformFeedback();
214} 214}
215 215
216void RasterizerVulkan::Clear() { 216void RasterizerVulkan::Clear(u32 layer_count) {
217 MICROPROFILE_SCOPE(Vulkan_Clearing); 217 MICROPROFILE_SCOPE(Vulkan_Clearing);
218 218
219 if (!maxwell3d->ShouldExecute()) { 219 if (!maxwell3d->ShouldExecute()) {
@@ -256,7 +256,7 @@ void RasterizerVulkan::Clear() {
256 .rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift) 256 .rect = regs.clear_control.use_scissor ? GetScissorState(regs, 0, up_scale, down_shift)
257 : default_scissor, 257 : default_scissor,
258 .baseArrayLayer = regs.clear_surface.layer, 258 .baseArrayLayer = regs.clear_surface.layer,
259 .layerCount = 1, 259 .layerCount = layer_count,
260 }; 260 };
261 if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) { 261 if (clear_rect.rect.extent.width == 0 || clear_rect.rect.extent.height == 0) {
262 return; 262 return;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index b0bc306f5..70f36d58a 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -65,7 +65,7 @@ public:
65 ~RasterizerVulkan() override; 65 ~RasterizerVulkan() override;
66 66
67 void Draw(bool is_indexed, u32 instance_count) override; 67 void Draw(bool is_indexed, u32 instance_count) override;
68 void Clear() override; 68 void Clear(u32 layer_count) override;
69 void DispatchCompute() override; 69 void DispatchCompute() override;
70 void ResetCounter(VideoCore::QueryType type) override; 70 void ResetCounter(VideoCore::QueryType type) override;
71 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; 71 void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 7934f2a51..4a7b633b7 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -221,6 +221,7 @@ void Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_s
221 [[fallthrough]]; 221 [[fallthrough]];
222 default: 222 default:
223 vk::Check(result); 223 vk::Check(result);
224 break;
224 } 225 }
225 }); 226 });
226 chunk->MarkSubmit(); 227 chunk->MarkSubmit();
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 853b80d8a..a65bbeb1c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -108,6 +108,7 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
108 break; 108 break;
109 default: 109 default:
110 ASSERT_MSG(false, "Invalid surface type"); 110 ASSERT_MSG(false, "Invalid surface type");
111 break;
111 } 112 }
112 } 113 }
113 if (info.storage) { 114 if (info.storage) {
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index fd1a4b987..59120cd09 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -170,6 +170,7 @@ void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixe
170#undef BPP_CASE 170#undef BPP_CASE
171 default: 171 default:
172 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 172 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
173 break;
173 } 174 }
174} 175}
175 176
@@ -217,6 +218,7 @@ void SwizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes_p
217#undef BPP_CASE 218#undef BPP_CASE
218 default: 219 default:
219 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 220 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
221 break;
220 } 222 }
221} 223}
222 224
@@ -240,6 +242,7 @@ void UnswizzleSubrect(std::span<u8> output, std::span<const u8> input, u32 bytes
240#undef BPP_CASE 242#undef BPP_CASE
241 default: 243 default:
242 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel); 244 ASSERT_MSG(false, "Invalid bytes_per_pixel={}", bytes_per_pixel);
245 break;
243 } 246 }
244} 247}
245 248
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 5cc1fbf32..adad36221 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -18,6 +18,9 @@ add_executable(yuzu
18 about_dialog.cpp 18 about_dialog.cpp
19 about_dialog.h 19 about_dialog.h
20 aboutdialog.ui 20 aboutdialog.ui
21 applets/qt_amiibo_settings.cpp
22 applets/qt_amiibo_settings.h
23 applets/qt_amiibo_settings.ui
21 applets/qt_controller.cpp 24 applets/qt_controller.cpp
22 applets/qt_controller.h 25 applets/qt_controller.h
23 applets/qt_controller.ui 26 applets/qt_controller.ui
diff --git a/src/yuzu/applets/qt_amiibo_settings.cpp b/src/yuzu/applets/qt_amiibo_settings.cpp
new file mode 100644
index 000000000..efb7f6ecc
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.cpp
@@ -0,0 +1,260 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <thread>
6#include <fmt/format.h>
7#include <nlohmann/json.hpp>
8
9#include "common/assert.h"
10#include "common/string_util.h"
11#include "core/hle/service/nfp/nfp_device.h"
12#include "core/hle/service/nfp/nfp_result.h"
13#include "input_common/drivers/virtual_amiibo.h"
14#include "input_common/main.h"
15#include "ui_qt_amiibo_settings.h"
16#include "web_service/web_backend.h"
17#include "yuzu/applets/qt_amiibo_settings.h"
18#include "yuzu/main.h"
19
20QtAmiiboSettingsDialog::QtAmiiboSettingsDialog(QWidget* parent,
21 Core::Frontend::CabinetParameters parameters_,
22 InputCommon::InputSubsystem* input_subsystem_,
23 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_)
24 : QDialog(parent), ui(std::make_unique<Ui::QtAmiiboSettingsDialog>()),
25 input_subsystem{input_subsystem_}, nfp_device{std::move(nfp_device_)},
26 parameters(std::move(parameters_)) {
27 ui->setupUi(this);
28
29 LoadInfo();
30
31 resize(0, 0);
32}
33
34QtAmiiboSettingsDialog::~QtAmiiboSettingsDialog() = default;
35
36int QtAmiiboSettingsDialog::exec() {
37 if (!is_initalized) {
38 return QDialog::Rejected;
39 }
40 return QDialog::exec();
41}
42
43std::string QtAmiiboSettingsDialog::GetName() const {
44 return ui->amiiboCustomNameValue->text().toStdString();
45}
46
47void QtAmiiboSettingsDialog::LoadInfo() {
48 if (input_subsystem->GetVirtualAmiibo()->ReloadAmiibo() !=
49 InputCommon::VirtualAmiibo::Info::Success) {
50 return;
51 }
52
53 if (nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagFound &&
54 nfp_device->GetCurrentState() != Service::NFP::DeviceState::TagMounted) {
55 return;
56 }
57 nfp_device->Mount(Service::NFP::MountTarget::All);
58
59 LoadAmiiboInfo();
60 LoadAmiiboData();
61 LoadAmiiboGameInfo();
62
63 ui->amiiboDirectoryValue->setText(
64 QString::fromStdString(input_subsystem->GetVirtualAmiibo()->GetLastFilePath()));
65
66 SetSettingsDescription();
67 is_initalized = true;
68}
69
70void QtAmiiboSettingsDialog::LoadAmiiboInfo() {
71 Service::NFP::ModelInfo model_info{};
72 const auto model_result = nfp_device->GetModelInfo(model_info);
73
74 if (model_result.IsFailure()) {
75 ui->amiiboImageLabel->setVisible(false);
76 ui->amiiboInfoGroup->setVisible(false);
77 return;
78 }
79
80 const auto amiibo_id =
81 fmt::format("{:04x}{:02x}{:02x}{:04x}{:02x}02", Common::swap16(model_info.character_id),
82 model_info.character_variant, model_info.amiibo_type, model_info.model_number,
83 model_info.series);
84
85 LOG_DEBUG(Frontend, "Loading amiibo id {}", amiibo_id);
86 // Note: This function is not being used until we host the images on our server
87 // LoadAmiiboApiInfo(amiibo_id);
88 ui->amiiboImageLabel->setVisible(false);
89 ui->amiiboInfoGroup->setVisible(false);
90}
91
92void QtAmiiboSettingsDialog::LoadAmiiboApiInfo(std::string_view amiibo_id) {
93 // TODO: Host this data on our website
94 WebService::Client client{"https://amiiboapi.com", {}, {}};
95 WebService::Client image_client{"https://raw.githubusercontent.com", {}, {}};
96 const auto url_path = fmt::format("/api/amiibo/?id={}", amiibo_id);
97
98 const auto amiibo_json = client.GetJson(url_path, true).returned_data;
99 if (amiibo_json.empty()) {
100 ui->amiiboImageLabel->setVisible(false);
101 ui->amiiboInfoGroup->setVisible(false);
102 return;
103 }
104
105 std::string amiibo_series{};
106 std::string amiibo_name{};
107 std::string amiibo_image_url{};
108 std::string amiibo_type{};
109
110 const auto parsed_amiibo_json_json = nlohmann::json::parse(amiibo_json).at("amiibo");
111 parsed_amiibo_json_json.at("amiiboSeries").get_to(amiibo_series);
112 parsed_amiibo_json_json.at("name").get_to(amiibo_name);
113 parsed_amiibo_json_json.at("image").get_to(amiibo_image_url);
114 parsed_amiibo_json_json.at("type").get_to(amiibo_type);
115
116 ui->amiiboSeriesValue->setText(QString::fromStdString(amiibo_series));
117 ui->amiiboNameValue->setText(QString::fromStdString(amiibo_name));
118 ui->amiiboTypeValue->setText(QString::fromStdString(amiibo_type));
119
120 if (amiibo_image_url.size() < 34) {
121 ui->amiiboImageLabel->setVisible(false);
122 }
123
124 const auto image_url_path = amiibo_image_url.substr(34, amiibo_image_url.size() - 34);
125 const auto image_data = image_client.GetImage(image_url_path, true).returned_data;
126
127 if (image_data.empty()) {
128 ui->amiiboImageLabel->setVisible(false);
129 }
130
131 QPixmap pixmap;
132 pixmap.loadFromData(reinterpret_cast<const u8*>(image_data.data()),
133 static_cast<uint>(image_data.size()));
134 pixmap = pixmap.scaled(250, 350, Qt::AspectRatioMode::KeepAspectRatio,
135 Qt::TransformationMode::SmoothTransformation);
136 ui->amiiboImageLabel->setPixmap(pixmap);
137}
138
139void QtAmiiboSettingsDialog::LoadAmiiboData() {
140 Service::NFP::RegisterInfo register_info{};
141 Service::NFP::CommonInfo common_info{};
142 const auto register_result = nfp_device->GetRegisterInfo(register_info);
143 const auto common_result = nfp_device->GetCommonInfo(common_info);
144
145 if (register_result.IsFailure()) {
146 ui->creationDateValue->setDisabled(true);
147 ui->modificationDateValue->setDisabled(true);
148 ui->amiiboCustomNameValue->setReadOnly(false);
149 ui->amiiboOwnerValue->setReadOnly(false);
150 return;
151 }
152
153 if (parameters.mode == Service::NFP::CabinetMode::StartNicknameAndOwnerSettings) {
154 ui->creationDateValue->setDisabled(true);
155 ui->modificationDateValue->setDisabled(true);
156 }
157
158 const auto amiibo_name = std::string(register_info.amiibo_name.data());
159 const auto owner_name = Common::UTF16ToUTF8(register_info.mii_char_info.name.data());
160 const auto creation_date =
161 QDate(register_info.creation_date.year, register_info.creation_date.month,
162 register_info.creation_date.day);
163
164 ui->amiiboCustomNameValue->setText(QString::fromStdString(amiibo_name));
165 ui->amiiboOwnerValue->setText(QString::fromStdString(owner_name));
166 ui->amiiboCustomNameValue->setReadOnly(true);
167 ui->amiiboOwnerValue->setReadOnly(true);
168 ui->creationDateValue->setDate(creation_date);
169
170 if (common_result.IsFailure()) {
171 ui->modificationDateValue->setDisabled(true);
172 return;
173 }
174
175 const auto modification_date =
176 QDate(common_info.last_write_date.year, common_info.last_write_date.month,
177 common_info.last_write_date.day);
178 ui->modificationDateValue->setDate(modification_date);
179}
180
181void QtAmiiboSettingsDialog::LoadAmiiboGameInfo() {
182 u32 application_area_id{};
183 const auto application_result = nfp_device->GetApplicationAreaId(application_area_id);
184
185 if (application_result.IsFailure()) {
186 ui->gameIdValue->setVisible(false);
187 ui->gameIdLabel->setText(tr("No game data present"));
188 return;
189 }
190
191 SetGameDataName(application_area_id);
192}
193
194void QtAmiiboSettingsDialog::SetGameDataName(u32 application_area_id) {
195 static constexpr std::array<std::pair<u32, const char*>, 12> game_name_list = {
196 // 3ds, wii u
197 std::pair<u32, const char*>{0x10110E00, "Super Smash Bros (3DS/WiiU)"},
198 {0x00132600, "Mario & Luigi: Paper Jam"},
199 {0x0014F000, "Animal Crossing: Happy Home Designer"},
200 {0x00152600, "Chibi-Robo!: Zip Lash"},
201 {0x10161f00, "Mario Party 10"},
202 {0x1019C800, "The Legend of Zelda: Twilight Princess HD"},
203 // switch
204 {0x10162B00, "Splatoon 2"},
205 {0x1016e100, "Shovel Knight: Treasure Trove"},
206 {0x1019C800, "The Legend of Zelda: Breath of the Wild"},
207 {0x34F80200, "Super Smash Bros. Ultimate"},
208 {0x38600500, "Splatoon 3"},
209 {0x3B440400, "The Legend of Zelda: Link's Awakening"},
210 };
211
212 for (const auto& [game_id, game_name] : game_name_list) {
213 if (application_area_id == game_id) {
214 ui->gameIdValue->setText(QString::fromStdString(game_name));
215 return;
216 }
217 }
218
219 const auto application_area_string = fmt::format("{:016x}", application_area_id);
220 ui->gameIdValue->setText(QString::fromStdString(application_area_string));
221}
222
223void QtAmiiboSettingsDialog::SetSettingsDescription() {
224 switch (parameters.mode) {
225 case Service::NFP::CabinetMode::StartFormatter:
226 ui->cabinetActionDescriptionLabel->setText(
227 tr("The following amiibo data will be formatted:"));
228 break;
229 case Service::NFP::CabinetMode::StartGameDataEraser:
230 ui->cabinetActionDescriptionLabel->setText(tr("The following game data will removed:"));
231 break;
232 case Service::NFP::CabinetMode::StartNicknameAndOwnerSettings:
233 ui->cabinetActionDescriptionLabel->setText(tr("Set nickname and owner:"));
234 break;
235 case Service::NFP::CabinetMode::StartRestorer:
236 ui->cabinetActionDescriptionLabel->setText(tr("Do you wish to restore this amiibo?"));
237 break;
238 }
239}
240
241QtAmiiboSettings::QtAmiiboSettings(GMainWindow& parent) {
242 connect(this, &QtAmiiboSettings::MainWindowShowAmiiboSettings, &parent,
243 &GMainWindow::AmiiboSettingsShowDialog, Qt::QueuedConnection);
244 connect(&parent, &GMainWindow::AmiiboSettingsFinished, this,
245 &QtAmiiboSettings::MainWindowFinished, Qt::QueuedConnection);
246}
247
248QtAmiiboSettings::~QtAmiiboSettings() = default;
249
250void QtAmiiboSettings::ShowCabinetApplet(
251 const Core::Frontend::CabinetCallback& callback_,
252 const Core::Frontend::CabinetParameters& parameters,
253 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const {
254 callback = std::move(callback_);
255 emit MainWindowShowAmiiboSettings(parameters, nfp_device);
256}
257
258void QtAmiiboSettings::MainWindowFinished(bool is_success, const std::string& name) {
259 callback(is_success, name);
260}
diff --git a/src/yuzu/applets/qt_amiibo_settings.h b/src/yuzu/applets/qt_amiibo_settings.h
new file mode 100644
index 000000000..930c96739
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.h
@@ -0,0 +1,83 @@
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 <memory>
8#include <QDialog>
9#include "core/frontend/applets/cabinet.h"
10
11class GMainWindow;
12class QCheckBox;
13class QComboBox;
14class QDialogButtonBox;
15class QGroupBox;
16class QLabel;
17
18namespace InputCommon {
19class InputSubsystem;
20}
21
22namespace Ui {
23class QtAmiiboSettingsDialog;
24}
25
26namespace Service::NFP {
27class NfpDevice;
28} // namespace Service::NFP
29
30class QtAmiiboSettingsDialog final : public QDialog {
31 Q_OBJECT
32
33public:
34 explicit QtAmiiboSettingsDialog(QWidget* parent, Core::Frontend::CabinetParameters parameters_,
35 InputCommon::InputSubsystem* input_subsystem_,
36 std::shared_ptr<Service::NFP::NfpDevice> nfp_device_);
37 ~QtAmiiboSettingsDialog() override;
38
39 int exec() override;
40
41 std::string GetName() const;
42
43private:
44 void LoadInfo();
45 void LoadAmiiboInfo();
46 void LoadAmiiboApiInfo(std::string_view amiibo_id);
47 void LoadAmiiboData();
48 void LoadAmiiboGameInfo();
49 void SetGameDataName(u32 application_area_id);
50 void SetSettingsDescription();
51
52 std::unique_ptr<Ui::QtAmiiboSettingsDialog> ui;
53
54 InputCommon::InputSubsystem* input_subsystem;
55 std::shared_ptr<Service::NFP::NfpDevice> nfp_device;
56
57 // Parameters sent in from the backend HLE applet.
58 Core::Frontend::CabinetParameters parameters;
59
60 // If false amiibo settings failed to load
61 bool is_initalized{};
62};
63
64class QtAmiiboSettings final : public QObject, public Core::Frontend::CabinetApplet {
65 Q_OBJECT
66
67public:
68 explicit QtAmiiboSettings(GMainWindow& parent);
69 ~QtAmiiboSettings() override;
70
71 void ShowCabinetApplet(const Core::Frontend::CabinetCallback& callback_,
72 const Core::Frontend::CabinetParameters& parameters,
73 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const override;
74
75signals:
76 void MainWindowShowAmiiboSettings(const Core::Frontend::CabinetParameters& parameters,
77 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) const;
78
79private:
80 void MainWindowFinished(bool is_success, const std::string& name);
81
82 mutable Core::Frontend::CabinetCallback callback;
83};
diff --git a/src/yuzu/applets/qt_amiibo_settings.ui b/src/yuzu/applets/qt_amiibo_settings.ui
new file mode 100644
index 000000000..f377a6e61
--- /dev/null
+++ b/src/yuzu/applets/qt_amiibo_settings.ui
@@ -0,0 +1,494 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>QtAmiiboSettingsDialog</class>
4 <widget class="QDialog" name="QtAmiiboSettingsDialog">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>839</width>
10 <height>500</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Amiibo Settings</string>
15 </property>
16 <property name="styleSheet">
17 <string notr="true"/>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
20 <property name="leftMargin">
21 <number>0</number>
22 </property>
23 <property name="topMargin">
24 <number>0</number>
25 </property>
26 <property name="rightMargin">
27 <number>0</number>
28 </property>
29 <property name="bottomMargin">
30 <number>0</number>
31 </property>
32 <item>
33 <widget class="QWidget" name="mainControllerApplet" native="true">
34 <layout class="QVBoxLayout" name="verticalLayout_1" stretch="0,3,0">
35 <property name="spacing">
36 <number>0</number>
37 </property>
38 <property name="leftMargin">
39 <number>0</number>
40 </property>
41 <property name="topMargin">
42 <number>0</number>
43 </property>
44 <property name="rightMargin">
45 <number>0</number>
46 </property>
47 <property name="bottomMargin">
48 <number>0</number>
49 </property>
50 <item>
51 <widget class="QWidget" name="topControllerApplet" native="true">
52 <layout class="QHBoxLayout" name="horizontalLayout">
53 <property name="spacing">
54 <number>10</number>
55 </property>
56 <property name="leftMargin">
57 <number>20</number>
58 </property>
59 <property name="topMargin">
60 <number>15</number>
61 </property>
62 <property name="rightMargin">
63 <number>0</number>
64 </property>
65 <property name="bottomMargin">
66 <number>15</number>
67 </property>
68 <item>
69 <widget class="QLabel" name="cabinetActionDescriptionLabel">
70 <property name="font">
71 <font>
72 <pointsize>12</pointsize>
73 <weight>75</weight>
74 <bold>true</bold>
75 </font>
76 </property>
77 <property name="text">
78 <string/>
79 </property>
80 <property name="alignment">
81 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
82 </property>
83 <property name="wordWrap">
84 <bool>false</bool>
85 </property>
86 </widget>
87 </item>
88 <item>
89 <spacer name="horizontalSpacer_2">
90 <property name="orientation">
91 <enum>Qt::Horizontal</enum>
92 </property>
93 <property name="sizeHint" stdset="0">
94 <size>
95 <width>40</width>
96 <height>20</height>
97 </size>
98 </property>
99 </spacer>
100 </item>
101 </layout>
102 </widget>
103 </item>
104 <item>
105 <widget class="QWidget" name="middleControllerApplet" native="true">
106 <layout class="QVBoxLayout" name="verticalLayout_3">
107 <property name="spacing">
108 <number>0</number>
109 </property>
110 <property name="leftMargin">
111 <number>0</number>
112 </property>
113 <property name="topMargin">
114 <number>0</number>
115 </property>
116 <property name="rightMargin">
117 <number>0</number>
118 </property>
119 <property name="bottomMargin">
120 <number>0</number>
121 </property>
122 <item>
123 <layout class="QHBoxLayout" name="horizontalLayout_2">
124 <property name="spacing">
125 <number>20</number>
126 </property>
127 <property name="leftMargin">
128 <number>15</number>
129 </property>
130 <property name="rightMargin">
131 <number>15</number>
132 </property>
133 <item>
134 <widget class="QLabel" name="amiiboImageLabel">
135 <property name="minimumSize">
136 <size>
137 <width>250</width>
138 <height>350</height>
139 </size>
140 </property>
141 <property name="maximumSize">
142 <size>
143 <width>236</width>
144 <height>350</height>
145 </size>
146 </property>
147 <property name="text">
148 <string/>
149 </property>
150 <property name="alignment">
151 <set>Qt::AlignCenter</set>
152 </property>
153 </widget>
154 </item>
155 <item>
156 <layout class="QVBoxLayout" name="verticalLayout_4">
157 <property name="leftMargin">
158 <number>0</number>
159 </property>
160 <property name="topMargin">
161 <number>8</number>
162 </property>
163 <property name="bottomMargin">
164 <number>15</number>
165 </property>
166 <item>
167 <widget class="QGroupBox" name="amiiboInfoGroup">
168 <property name="title">
169 <string>Amiibo Info</string>
170 </property>
171 <layout class="QVBoxLayout" name="verticalLayout_5">
172 <item>
173 <layout class="QGridLayout" name="gridLayout_1">
174 <item row="0" column="0">
175 <widget class="QLabel" name="amiiboSeriesLabel">
176 <property name="text">
177 <string>Series</string>
178 </property>
179 </widget>
180 </item>
181 <item row="0" column="1">
182 <widget class="QLineEdit" name="amiiboSeriesValue">
183 <property name="sizePolicy">
184 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
185 <horstretch>0</horstretch>
186 <verstretch>0</verstretch>
187 </sizepolicy>
188 </property>
189 <property name="readOnly">
190 <bool>true</bool>
191 </property>
192 </widget>
193 </item>
194 <item row="1" column="0">
195 <widget class="QLabel" name="amiiboTypeLabel">
196 <property name="text">
197 <string>Type</string>
198 </property>
199 </widget>
200 </item>
201 <item row="1" column="1">
202 <widget class="QLineEdit" name="amiiboTypeValue">
203 <property name="sizePolicy">
204 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
205 <horstretch>0</horstretch>
206 <verstretch>0</verstretch>
207 </sizepolicy>
208 </property>
209 <property name="readOnly">
210 <bool>true</bool>
211 </property>
212 </widget>
213 </item>
214 <item row="2" column="0">
215 <widget class="QLabel" name="amiiboNameLabel">
216 <property name="text">
217 <string>Name</string>
218 </property>
219 </widget>
220 </item>
221 <item row="2" column="1">
222 <widget class="QLineEdit" name="amiiboNameValue">
223 <property name="sizePolicy">
224 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
225 <horstretch>0</horstretch>
226 <verstretch>0</verstretch>
227 </sizepolicy>
228 </property>
229 <property name="readOnly">
230 <bool>true</bool>
231 </property>
232 </widget>
233 </item>
234 </layout>
235 </item>
236 </layout>
237 </widget>
238 </item>
239 <item>
240 <widget class="QGroupBox" name="amiiboDataGroup">
241 <property name="title">
242 <string>Amiibo Data</string>
243 </property>
244 <layout class="QVBoxLayout" name="verticalLayout_6">
245 <item>
246 <layout class="QGridLayout" name="gridLayout_2">
247 <item row="0" column="0">
248 <widget class="QLabel" name="amiiboCustomNameLabel">
249 <property name="text">
250 <string>Custom Name</string>
251 </property>
252 </widget>
253 </item>
254 <item row="0" column="1">
255 <widget class="QLineEdit" name="amiiboCustomNameValue">
256 <property name="sizePolicy">
257 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
258 <horstretch>0</horstretch>
259 <verstretch>0</verstretch>
260 </sizepolicy>
261 </property>
262 <property name="maxLength">
263 <number>10</number>
264 </property>
265 </widget>
266 </item>
267 <item row="1" column="0">
268 <widget class="QLabel" name="amiiboOwnerLabel">
269 <property name="text">
270 <string>Owner</string>
271 </property>
272 </widget>
273 </item>
274 <item row="1" column="1">
275 <widget class="QLineEdit" name="amiiboOwnerValue">
276 <property name="sizePolicy">
277 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
278 <horstretch>0</horstretch>
279 <verstretch>0</verstretch>
280 </sizepolicy>
281 </property>
282 <property name="maxLength">
283 <number>10</number>
284 </property>
285 </widget>
286 </item>
287 <item row="2" column="0">
288 <widget class="QLabel" name="creationDateLabel">
289 <property name="text">
290 <string>Creation Date</string>
291 </property>
292 </widget>
293 </item>
294 <item row="2" column="1">
295 <widget class="QDateTimeEdit" name="creationDateValue">
296 <property name="readOnly">
297 <bool>true</bool>
298 </property>
299 <property name="minimumDate">
300 <date>
301 <year>1970</year>
302 <month>1</month>
303 <day>1</day>
304 </date>
305 </property>
306 <property name="displayFormat">
307 <string>dd/MM/yyyy</string>
308 </property>
309 </widget>
310 </item>
311 <item row="3" column="0">
312 <widget class="QLabel" name="modificationDateLabel">
313 <property name="text">
314 <string>Modification Date</string>
315 </property>
316 </widget>
317 </item>
318 <item row="3" column="1">
319 <widget class="QDateTimeEdit" name="modificationDateValue">
320 <property name="readOnly">
321 <bool>true</bool>
322 </property>
323 <property name="minimumDate">
324 <date>
325 <year>1970</year>
326 <month>1</month>
327 <day>1</day>
328 </date>
329 </property>
330 <property name="displayFormat">
331 <string>dd/MM/yyyy </string>
332 </property>
333 </widget>
334 </item>
335 </layout>
336 </item>
337 </layout>
338 </widget>
339 </item>
340 <item>
341 <widget class="QGroupBox" name="gameDataGroup">
342 <property name="minimumSize">
343 <size>
344 <width>500</width>
345 <height>0</height>
346 </size>
347 </property>
348 <property name="title">
349 <string>Game Data</string>
350 </property>
351 <layout class="QGridLayout" name="gridLayout_3">
352 <item row="0" column="0">
353 <widget class="QLabel" name="gameIdLabel">
354 <property name="text">
355 <string>Game Id</string>
356 </property>
357 </widget>
358 </item>
359 <item row="0" column="1">
360 <widget class="QLineEdit" name="gameIdValue">
361 <property name="sizePolicy">
362 <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
363 <horstretch>0</horstretch>
364 <verstretch>0</verstretch>
365 </sizepolicy>
366 </property>
367 <property name="readOnly">
368 <bool>true</bool>
369 </property>
370 </widget>
371 </item>
372 </layout>
373 </widget>
374 </item>
375 <item>
376 <widget class="QGroupBox" name="MountAmiiboGroup">
377 <property name="minimumSize">
378 <size>
379 <width>500</width>
380 <height>0</height>
381 </size>
382 </property>
383 <property name="title">
384 <string>Mount Amiibo</string>
385 </property>
386 <layout class="QGridLayout" name="gridLayout_4">
387 <item row="0" column="3">
388 <widget class="QToolButton" name="amiiboDirectoryButton">
389 <property name="text">
390 <string>...</string>
391 </property>
392 </widget>
393 </item>
394 <item row="0" column="1">
395 <spacer name="horizontalSpacer">
396 <property name="orientation">
397 <enum>Qt::Horizontal</enum>
398 </property>
399 <property name="sizeType">
400 <enum>QSizePolicy::Maximum</enum>
401 </property>
402 <property name="sizeHint" stdset="0">
403 <size>
404 <width>60</width>
405 <height>20</height>
406 </size>
407 </property>
408 </spacer>
409 </item>
410 <item row="0" column="0">
411 <widget class="QLabel" name="amiiboDirectoryLabel">
412 <property name="text">
413 <string>File Path</string>
414 </property>
415 </widget>
416 </item>
417 <item row="0" column="2">
418 <widget class="QLineEdit" name="amiiboDirectoryValue"/>
419 </item>
420 </layout>
421 </widget>
422 </item>
423 <item>
424 <spacer name="verticalSpacer">
425 <property name="orientation">
426 <enum>Qt::Vertical</enum>
427 </property>
428 <property name="sizeHint" stdset="0">
429 <size>
430 <width>20</width>
431 <height>40</height>
432 </size>
433 </property>
434 </spacer>
435 </item>
436 </layout>
437 </item>
438 </layout>
439 </item>
440 </layout>
441 </widget>
442 </item>
443 <item>
444 <widget class="QWidget" name="bottomControllerApplet" native="true">
445 <layout class="QHBoxLayout" name="horizontalLayout_6">
446 <property name="spacing">
447 <number>15</number>
448 </property>
449 <property name="leftMargin">
450 <number>15</number>
451 </property>
452 <property name="topMargin">
453 <number>8</number>
454 </property>
455 <property name="rightMargin">
456 <number>20</number>
457 </property>
458 <property name="bottomMargin">
459 <number>8</number>
460 </property>
461 <item alignment="Qt::AlignBottom">
462 <widget class="QDialogButtonBox" name="buttonBox">
463 <property name="enabled">
464 <bool>true</bool>
465 </property>
466 <property name="standardButtons">
467 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
468 </property>
469 </widget>
470 </item>
471 </layout>
472 </widget>
473 </item>
474 </layout>
475 </widget>
476 </item>
477 </layout>
478 </widget>
479 <resources/>
480 <connections>
481 <connection>
482 <sender>buttonBox</sender>
483 <signal>accepted()</signal>
484 <receiver>QtAmiiboSettingsDialog</receiver>
485 <slot>accept()</slot>
486 </connection>
487 <connection>
488 <sender>buttonBox</sender>
489 <signal>rejected()</signal>
490 <receiver>QtAmiiboSettingsDialog</receiver>
491 <slot>reject()</slot>
492 </connection>
493 </connections>
494</ui>
diff --git a/src/yuzu/compatdb.cpp b/src/yuzu/compatdb.cpp
index b03e71248..05f49c0d2 100644
--- a/src/yuzu/compatdb.cpp
+++ b/src/yuzu/compatdb.cpp
@@ -126,6 +126,7 @@ void CompatDB::Submit() {
126 break; 126 break;
127 default: 127 default:
128 LOG_ERROR(Frontend, "Unexpected page: {}", currentId()); 128 LOG_ERROR(Frontend, "Unexpected page: {}", currentId());
129 break;
129 } 130 }
130} 131}
131 132
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp
index 5c0217ba8..a47089988 100644
--- a/src/yuzu/configuration/configure_profile_manager.cpp
+++ b/src/yuzu/configuration/configure_profile_manager.cpp
@@ -2,6 +2,9 @@
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 <functional>
6#include <QDialog>
7#include <QDialogButtonBox>
5#include <QFileDialog> 8#include <QFileDialog>
6#include <QGraphicsItem> 9#include <QGraphicsItem>
7#include <QHeaderView> 10#include <QHeaderView>
@@ -108,9 +111,12 @@ ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QW
108 111
109 connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser); 112 connect(ui->pm_add, &QPushButton::clicked, this, &ConfigureProfileManager::AddUser);
110 connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser); 113 connect(ui->pm_rename, &QPushButton::clicked, this, &ConfigureProfileManager::RenameUser);
111 connect(ui->pm_remove, &QPushButton::clicked, this, &ConfigureProfileManager::DeleteUser); 114 connect(ui->pm_remove, &QPushButton::clicked, this,
115 &ConfigureProfileManager::ConfirmDeleteUser);
112 connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage); 116 connect(ui->pm_set_image, &QPushButton::clicked, this, &ConfigureProfileManager::SetUserImage);
113 117
118 confirm_dialog = new ConfigureProfileManagerDeleteDialog(this);
119
114 scene = new QGraphicsScene; 120 scene = new QGraphicsScene;
115 ui->current_user_icon->setScene(scene); 121 ui->current_user_icon->setScene(scene);
116 122
@@ -230,26 +236,23 @@ void ConfigureProfileManager::RenameUser() {
230 UpdateCurrentUser(); 236 UpdateCurrentUser();
231} 237}
232 238
233void ConfigureProfileManager::DeleteUser() { 239void ConfigureProfileManager::ConfirmDeleteUser() {
234 const auto index = tree_view->currentIndex().row(); 240 const auto index = tree_view->currentIndex().row();
235 const auto uuid = profile_manager->GetUser(index); 241 const auto uuid = profile_manager->GetUser(index);
236 ASSERT(uuid); 242 ASSERT(uuid);
237 const auto username = GetAccountUsername(*profile_manager, *uuid); 243 const auto username = GetAccountUsername(*profile_manager, *uuid);
238 244
239 const auto confirm = QMessageBox::question( 245 confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); });
240 this, tr("Confirm Delete"), 246 confirm_dialog->show();
241 tr("You are about to delete user with name \"%1\". Are you sure?").arg(username)); 247}
242
243 if (confirm == QMessageBox::No) {
244 return;
245 }
246 248
249void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
247 if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) { 250 if (Settings::values.current_user.GetValue() == tree_view->currentIndex().row()) {
248 Settings::values.current_user = 0; 251 Settings::values.current_user = 0;
249 } 252 }
250 UpdateCurrentUser(); 253 UpdateCurrentUser();
251 254
252 if (!profile_manager->RemoveUser(*uuid)) { 255 if (!profile_manager->RemoveUser(uuid)) {
253 return; 256 return;
254 } 257 }
255 258
@@ -319,3 +322,47 @@ void ConfigureProfileManager::SetUserImage() {
319 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)}); 322 new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
320 UpdateCurrentUser(); 323 UpdateCurrentUser();
321} 324}
325
326ConfigureProfileManagerDeleteDialog::ConfigureProfileManagerDeleteDialog(QWidget* parent)
327 : QDialog{parent} {
328 auto dialog_vbox_layout = new QVBoxLayout(this);
329 dialog_button_box =
330 new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No, Qt::Horizontal, parent);
331 auto label_message =
332 new QLabel(tr("Delete this user? All of the user's save data will be deleted."), this);
333 label_info = new QLabel(this);
334 auto dialog_hbox_layout_widget = new QWidget(this);
335 auto dialog_hbox_layout = new QHBoxLayout(dialog_hbox_layout_widget);
336 icon_scene = new QGraphicsScene(0, 0, 64, 64, this);
337 auto icon_view = new QGraphicsView(icon_scene, this);
338
339 dialog_hbox_layout_widget->setLayout(dialog_hbox_layout);
340 icon_view->setMaximumSize(64, 64);
341 icon_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
342 icon_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
343 this->setLayout(dialog_vbox_layout);
344 this->setWindowTitle(tr("Confirm Delete"));
345 this->setSizeGripEnabled(false);
346 dialog_vbox_layout->addWidget(label_message);
347 dialog_vbox_layout->addWidget(dialog_hbox_layout_widget);
348 dialog_vbox_layout->addWidget(dialog_button_box);
349 dialog_hbox_layout->addWidget(icon_view);
350 dialog_hbox_layout->addWidget(label_info);
351
352 connect(dialog_button_box, &QDialogButtonBox::rejected, this, [this]() { close(); });
353}
354
355ConfigureProfileManagerDeleteDialog::~ConfigureProfileManagerDeleteDialog() = default;
356
357void ConfigureProfileManagerDeleteDialog::SetInfo(const QString& username, const Common::UUID& uuid,
358 std::function<void()> accept_callback) {
359 label_info->setText(
360 tr("Name: %1\nUUID: %2").arg(username, QString::fromStdString(uuid.FormattedString())));
361 icon_scene->clear();
362 icon_scene->addPixmap(GetIcon(uuid));
363
364 connect(dialog_button_box, &QDialogButtonBox::accepted, this, [this, accept_callback]() {
365 close();
366 accept_callback();
367 });
368}
diff --git a/src/yuzu/configuration/configure_profile_manager.h b/src/yuzu/configuration/configure_profile_manager.h
index fe9033779..c4b1a334e 100644
--- a/src/yuzu/configuration/configure_profile_manager.h
+++ b/src/yuzu/configuration/configure_profile_manager.h
@@ -3,16 +3,24 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <functional>
6#include <memory> 7#include <memory>
7 8
9#include <QDialog>
8#include <QList> 10#include <QList>
9#include <QWidget> 11#include <QWidget>
10 12
13namespace Common {
14struct UUID;
15}
16
11namespace Core { 17namespace Core {
12class System; 18class System;
13} 19}
14 20
15class QGraphicsScene; 21class QGraphicsScene;
22class QDialogButtonBox;
23class QLabel;
16class QStandardItem; 24class QStandardItem;
17class QStandardItemModel; 25class QStandardItemModel;
18class QTreeView; 26class QTreeView;
@@ -26,6 +34,20 @@ namespace Ui {
26class ConfigureProfileManager; 34class ConfigureProfileManager;
27} 35}
28 36
37class ConfigureProfileManagerDeleteDialog : public QDialog {
38public:
39 explicit ConfigureProfileManagerDeleteDialog(QWidget* parent);
40 ~ConfigureProfileManagerDeleteDialog();
41
42 void SetInfo(const QString& username, const Common::UUID& uuid,
43 std::function<void()> accept_callback);
44
45private:
46 QDialogButtonBox* dialog_button_box;
47 QGraphicsScene* icon_scene;
48 QLabel* label_info;
49};
50
29class ConfigureProfileManager : public QWidget { 51class ConfigureProfileManager : public QWidget {
30 Q_OBJECT 52 Q_OBJECT
31 53
@@ -47,7 +69,8 @@ private:
47 void SelectUser(const QModelIndex& index); 69 void SelectUser(const QModelIndex& index);
48 void AddUser(); 70 void AddUser();
49 void RenameUser(); 71 void RenameUser();
50 void DeleteUser(); 72 void ConfirmDeleteUser();
73 void DeleteUser(const Common::UUID& uuid);
51 void SetUserImage(); 74 void SetUserImage();
52 75
53 QVBoxLayout* layout; 76 QVBoxLayout* layout;
@@ -55,6 +78,8 @@ private:
55 QStandardItemModel* item_model; 78 QStandardItemModel* item_model;
56 QGraphicsScene* scene; 79 QGraphicsScene* scene;
57 80
81 ConfigureProfileManagerDeleteDialog* confirm_dialog;
82
58 std::vector<QList<QStandardItem*>> list_items; 83 std::vector<QList<QStandardItem*>> list_items;
59 84
60 std::unique_ptr<Ui::ConfigureProfileManager> ui; 85 std::unique_ptr<Ui::ConfigureProfileManager> ui;
diff --git a/src/yuzu/configuration/configure_profile_manager.ui b/src/yuzu/configuration/configure_profile_manager.ui
index cfe7478c8..bd6dea4f4 100644
--- a/src/yuzu/configuration/configure_profile_manager.ui
+++ b/src/yuzu/configuration/configure_profile_manager.ui
@@ -57,6 +57,12 @@
57 <height>48</height> 57 <height>48</height>
58 </size> 58 </size>
59 </property> 59 </property>
60 <property name="frameShape">
61 <enum>QFrame::NoFrame</enum>
62 </property>
63 <property name="frameShadow">
64 <enum>QFrame::Plain</enum>
65 </property>
60 <property name="verticalScrollBarPolicy"> 66 <property name="verticalScrollBarPolicy">
61 <enum>Qt::ScrollBarAlwaysOff</enum> 67 <enum>Qt::ScrollBarAlwaysOff</enum>
62 </property> 68 </property>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 032ff1cbc..4081af391 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -15,6 +15,7 @@
15#endif 15#endif
16 16
17// VFS includes must be before glad as they will conflict with Windows file api, which uses defines. 17// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
18#include "applets/qt_amiibo_settings.h"
18#include "applets/qt_controller.h" 19#include "applets/qt_controller.h"
19#include "applets/qt_error.h" 20#include "applets/qt_error.h"
20#include "applets/qt_profile_select.h" 21#include "applets/qt_profile_select.h"
@@ -26,6 +27,7 @@
26#include "configuration/configure_tas.h" 27#include "configuration/configure_tas.h"
27#include "core/file_sys/vfs.h" 28#include "core/file_sys/vfs.h"
28#include "core/file_sys/vfs_real.h" 29#include "core/file_sys/vfs_real.h"
30#include "core/frontend/applets/cabinet.h"
29#include "core/frontend/applets/controller.h" 31#include "core/frontend/applets/controller.h"
30#include "core/frontend/applets/general_frontend.h" 32#include "core/frontend/applets/general_frontend.h"
31#include "core/frontend/applets/mii_edit.h" 33#include "core/frontend/applets/mii_edit.h"
@@ -361,11 +363,10 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
361 } 363 }
362 } 364 }
363 LOG_INFO(Frontend, "Host CPU: {}", cpu_string); 365 LOG_INFO(Frontend, "Host CPU: {}", cpu_string);
364#endif
365
366 if (std::optional<int> processor_core = Common::GetProcessorCount()) { 366 if (std::optional<int> processor_core = Common::GetProcessorCount()) {
367 LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core); 367 LOG_INFO(Frontend, "Host CPU Cores: {}", *processor_core);
368 } 368 }
369#endif
369 LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count); 370 LOG_INFO(Frontend, "Host CPU Threads: {}", processor_count);
370 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); 371 LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString());
371 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", 372 LOG_INFO(Frontend, "Host RAM: {:.2f} GiB",
@@ -549,6 +550,11 @@ void GMainWindow::RegisterMetaTypes() {
549 550
550 // Register applet types 551 // Register applet types
551 552
553 // Cabinet Applet
554 qRegisterMetaType<Core::Frontend::CabinetParameters>("Core::Frontend::CabinetParameters");
555 qRegisterMetaType<std::shared_ptr<Service::NFP::NfpDevice>>(
556 "std::shared_ptr<Service::NFP::NfpDevice>");
557
552 // Controller Applet 558 // Controller Applet
553 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters"); 559 qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
554 560
@@ -570,6 +576,21 @@ void GMainWindow::RegisterMetaTypes() {
570 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus"); 576 qRegisterMetaType<Core::SystemResultStatus>("Core::SystemResultStatus");
571} 577}
572 578
579void GMainWindow::AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
580 std::shared_ptr<Service::NFP::NfpDevice> nfp_device) {
581 QtAmiiboSettingsDialog dialog(this, parameters, input_subsystem.get(), nfp_device);
582
583 dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
584 Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
585 dialog.setWindowModality(Qt::WindowModal);
586 if (dialog.exec() == QDialog::Rejected) {
587 emit AmiiboSettingsFinished(false, {});
588 return;
589 }
590
591 emit AmiiboSettingsFinished(true, dialog.GetName());
592}
593
573void GMainWindow::ControllerSelectorReconfigureControllers( 594void GMainWindow::ControllerSelectorReconfigureControllers(
574 const Core::Frontend::ControllerParameters& parameters) { 595 const Core::Frontend::ControllerParameters& parameters) {
575 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system); 596 QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get(), *system);
@@ -1547,6 +1568,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
1547 system->SetFilesystem(vfs); 1568 system->SetFilesystem(vfs);
1548 1569
1549 system->SetAppletFrontendSet({ 1570 system->SetAppletFrontendSet({
1571 std::make_unique<QtAmiiboSettings>(*this), // Amiibo Settings
1550 std::make_unique<QtControllerSelector>(*this), // Controller Selector 1572 std::make_unique<QtControllerSelector>(*this), // Controller Selector
1551 std::make_unique<QtErrorDisplay>(*this), // Error Display 1573 std::make_unique<QtErrorDisplay>(*this), // Error Display
1552 nullptr, // Mii Editor 1574 nullptr, // Mii Editor
@@ -1957,6 +1979,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
1957 } 1979 }
1958 default: 1980 default:
1959 UNIMPLEMENTED(); 1981 UNIMPLEMENTED();
1982 break;
1960 } 1983 }
1961 1984
1962 const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); 1985 const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path));
@@ -3200,6 +3223,7 @@ void GMainWindow::OnToggleGpuAccuracy() {
3200 case Settings::GPUAccuracy::Extreme: 3223 case Settings::GPUAccuracy::Extreme:
3201 default: { 3224 default: {
3202 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High); 3225 Settings::values.gpu_accuracy.SetValue(Settings::GPUAccuracy::High);
3226 break;
3203 } 3227 }
3204 } 3228 }
3205 3229
@@ -3532,6 +3556,7 @@ void GMainWindow::UpdateGPUAccuracyButton() {
3532 default: { 3556 default: {
3533 gpu_accuracy_button->setText(tr("GPU ERROR")); 3557 gpu_accuracy_button->setText(tr("GPU ERROR"));
3534 gpu_accuracy_button->setChecked(true); 3558 gpu_accuracy_button->setChecked(true);
3559 break;
3535 } 3560 }
3536 } 3561 }
3537} 3562}
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b73f550dd..6a9992d05 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -55,6 +55,7 @@ class System;
55} // namespace Core 55} // namespace Core
56 56
57namespace Core::Frontend { 57namespace Core::Frontend {
58struct CabinetParameters;
58struct ControllerParameters; 59struct ControllerParameters;
59struct InlineAppearParameters; 60struct InlineAppearParameters;
60struct InlineTextParameters; 61struct InlineTextParameters;
@@ -82,6 +83,10 @@ enum class SwkbdReplyType : u32;
82enum class WebExitReason : u32; 83enum class WebExitReason : u32;
83} // namespace Service::AM::Applets 84} // namespace Service::AM::Applets
84 85
86namespace Service::NFP {
87class NfpDevice;
88} // namespace Service::NFP
89
85namespace Ui { 90namespace Ui {
86class MainWindow; 91class MainWindow;
87} 92}
@@ -149,6 +154,8 @@ signals:
149 154
150 void UpdateInstallProgress(); 155 void UpdateInstallProgress();
151 156
157 void AmiiboSettingsFinished(bool is_success, const std::string& name);
158
152 void ControllerSelectorReconfigureFinished(); 159 void ControllerSelectorReconfigureFinished();
153 160
154 void ErrorDisplayFinished(); 161 void ErrorDisplayFinished();
@@ -170,6 +177,8 @@ public slots:
170 void OnExecuteProgram(std::size_t program_index); 177 void OnExecuteProgram(std::size_t program_index);
171 void OnExit(); 178 void OnExit();
172 void OnSaveConfig(); 179 void OnSaveConfig();
180 void AmiiboSettingsShowDialog(const Core::Frontend::CabinetParameters& parameters,
181 std::shared_ptr<Service::NFP::NfpDevice> nfp_device);
173 void ControllerSelectorReconfigureControllers( 182 void ControllerSelectorReconfigureControllers(
174 const Core::Frontend::ControllerParameters& parameters); 183 const Core::Frontend::ControllerParameters& parameters);
175 void SoftwareKeyboardInitialize( 184 void SoftwareKeyboardInitialize(
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 65455c86e..25948328c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -84,6 +84,7 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
84 default: 84 default:
85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented");
86 std::exit(EXIT_FAILURE); 86 std::exit(EXIT_FAILURE);
87 break;
87 } 88 }
88 89
89 OnResize(); 90 OnResize();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index e16f79eb4..dfe5a30ea 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -351,6 +351,7 @@ int main(int argc, char** argv) {
351 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 351 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
352 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)); 352 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
353 } 353 }
354 break;
354 } 355 }
355 356
356 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); 357 system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL");