summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/fiber.cpp5
-rw-r--r--src/common/settings.h3
-rw-r--r--src/common/settings_input.h1
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp50
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp82
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
-rw-r--r--src/core/core_timing.h6
-rw-r--r--src/core/hid/emulated_devices.cpp45
-rw-r--r--src/core/hid/emulated_devices.h34
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h3
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp11
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp8
-rw-r--r--src/core/hle/kernel/k_spin_lock.cpp39
-rw-r--r--src/core/hle/kernel/k_spin_lock.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp39
-rw-r--r--src/core/hle/kernel/k_thread.h13
-rw-r--r--src/core/hle/kernel/kernel.cpp18
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/kernel/physical_core.cpp3
-rw-r--r--src/core/hle/kernel/physical_core.h7
-rw-r--r--src/core/hle/service/hid/hid.cpp27
-rw-r--r--src/core/hle/service/hid/hidbus.cpp531
-rw-r--r--src/core/hle/service/hid/hidbus.h131
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp72
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h179
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp286
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h254
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h39
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp52
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h39
-rw-r--r--src/core/hle/service/jit/jit.cpp291
-rw-r--r--src/core/hle/service/jit/jit_context.cpp424
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/ldr/ldr.cpp3
-rw-r--r--src/core/hle/service/service.h5
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h15
-rw-r--r--src/video_core/engines/maxwell_3d.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.h7
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp26
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h2
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp49
-rw-r--r--src/video_core/surface.h8
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/video_core/texture_cache/formatter.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp12
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp34
-rw-r--r--src/yuzu/configuration/config.h3
-rw-r--r--src/yuzu/configuration/configure_input.cpp5
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp9
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui14
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp2
-rw-r--r--src/yuzu/configuration/configure_ringcon.cpp424
-rw-r--r--src/yuzu/configuration/configure_ringcon.h85
-rw-r--r--src/yuzu/configuration/configure_ringcon.ui278
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu/uisettings.cpp8
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp13
64 files changed, 3676 insertions, 200 deletions
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 81b212e4b..177a74deb 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -2,9 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <mutex>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/fiber.h" 8#include "common/fiber.h"
7#include "common/spin_lock.h"
8#include "common/virtual_buffer.h" 9#include "common/virtual_buffer.h"
9 10
10#include <boost/context/detail/fcontext.hpp> 11#include <boost/context/detail/fcontext.hpp>
@@ -19,7 +20,7 @@ struct Fiber::FiberImpl {
19 VirtualBuffer<u8> stack; 20 VirtualBuffer<u8> stack;
20 VirtualBuffer<u8> rewind_stack; 21 VirtualBuffer<u8> rewind_stack;
21 22
22 SpinLock guard{}; 23 std::mutex guard;
23 std::function<void(void*)> entry_point; 24 std::function<void(void*)> entry_point;
24 std::function<void(void*)> rewind_point; 25 std::function<void(void*)> rewind_point;
25 void* rewind_parameter{}; 26 void* rewind_parameter{};
diff --git a/src/common/settings.h b/src/common/settings.h
index 86e0fa140..3b7be63b3 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -590,6 +590,9 @@ struct Values {
590 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"}; 590 BasicSetting<int> touch_from_button_map_index{0, "touch_from_button_map"};
591 std::vector<TouchFromButtonMap> touch_from_button_maps; 591 std::vector<TouchFromButtonMap> touch_from_button_maps;
592 592
593 BasicSetting<bool> enable_ring_controller{true, "enable_ring_controller"};
594 RingconRaw ringcon_analogs;
595
593 // Data Storage 596 // Data Storage
594 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"}; 597 BasicSetting<bool> use_virtual_sd{true, "use_virtual_sd"};
595 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"}; 598 BasicSetting<bool> gamecard_inserted{false, "gamecard_inserted"};
diff --git a/src/common/settings_input.h b/src/common/settings_input.h
index 4ff37e186..6f42346bc 100644
--- a/src/common/settings_input.h
+++ b/src/common/settings_input.h
@@ -357,6 +357,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 357using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 358using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>; 359using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
360using RingconRaw = std::string;
360 361
361constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; 362constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
362constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; 363constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ffbda7925..62230bae0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -434,6 +434,8 @@ add_library(core STATIC
434 hle/service/grc/grc.h 434 hle/service/grc/grc.h
435 hle/service/hid/hid.cpp 435 hle/service/hid/hid.cpp
436 hle/service/hid/hid.h 436 hle/service/hid/hid.h
437 hle/service/hid/hidbus.cpp
438 hle/service/hid/hidbus.h
437 hle/service/hid/irs.cpp 439 hle/service/hid/irs.cpp
438 hle/service/hid/irs.h 440 hle/service/hid/irs.h
439 hle/service/hid/ring_lifo.h 441 hle/service/hid/ring_lifo.h
@@ -460,6 +462,16 @@ add_library(core STATIC
460 hle/service/hid/controllers/touchscreen.h 462 hle/service/hid/controllers/touchscreen.h
461 hle/service/hid/controllers/xpad.cpp 463 hle/service/hid/controllers/xpad.cpp
462 hle/service/hid/controllers/xpad.h 464 hle/service/hid/controllers/xpad.h
465 hle/service/hid/hidbus/hidbus_base.cpp
466 hle/service/hid/hidbus/hidbus_base.h
467 hle/service/hid/hidbus/ringcon.cpp
468 hle/service/hid/hidbus/ringcon.h
469 hle/service/hid/hidbus/starlink.cpp
470 hle/service/hid/hidbus/starlink.h
471 hle/service/hid/hidbus/stubbed.cpp
472 hle/service/hid/hidbus/stubbed.h
473 hle/service/jit/jit_context.cpp
474 hle/service/jit/jit_context.h
463 hle/service/jit/jit.cpp 475 hle/service/jit/jit.cpp
464 hle/service/jit/jit.h 476 hle/service/jit/jit.h
465 hle/service/lbl/lbl.cpp 477 hle/service/lbl/lbl.cpp
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index ab3210d84..5de4384db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -88,7 +88,7 @@ public:
88 88
89 void CallSVC(u32 swi) override { 89 void CallSVC(u32 swi) override {
90 parent.svc_swi = swi; 90 parent.svc_swi = swi;
91 parent.jit->HaltExecution(svc_call); 91 parent.jit.load()->HaltExecution(svc_call);
92 } 92 }
93 93
94 void AddTicks(u64 ticks) override { 94 void AddTicks(u64 ticks) override {
@@ -151,6 +151,13 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
151 config.code_cache_size = 512_MiB; 151 config.code_cache_size = 512_MiB;
152 config.far_code_offset = 400_MiB; 152 config.far_code_offset = 400_MiB;
153 153
154 // null_jit
155 if (!page_table) {
156 // Don't waste too much memory on null_jit
157 config.code_cache_size = 8_MiB;
158 config.far_code_offset = 4_MiB;
159 }
160
154 // Safe optimizations 161 // Safe optimizations
155 if (Settings::values.cpu_debug_mode) { 162 if (Settings::values.cpu_debug_mode) {
156 if (!Settings::values.cpuopt_page_tables) { 163 if (!Settings::values.cpuopt_page_tables) {
@@ -228,18 +235,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
228 235
229void ARM_Dynarmic_32::Run() { 236void ARM_Dynarmic_32::Run() {
230 while (true) { 237 while (true) {
231 const auto hr = jit->Run(); 238 const auto hr = jit.load()->Run();
232 if (Has(hr, svc_call)) { 239 if (Has(hr, svc_call)) {
233 Kernel::Svc::Call(system, svc_swi); 240 Kernel::Svc::Call(system, svc_swi);
234 } 241 }
235 if (Has(hr, break_loop)) { 242 if (Has(hr, break_loop) || !uses_wall_clock) {
236 break; 243 break;
237 } 244 }
238 } 245 }
239} 246}
240 247
241void ARM_Dynarmic_32::Step() { 248void ARM_Dynarmic_32::Step() {
242 jit->Step(); 249 jit.load()->Step();
243} 250}
244 251
245ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 252ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -249,24 +256,24 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handl
249 cb(std::make_unique<DynarmicCallbacks32>(*this)), 256 cb(std::make_unique<DynarmicCallbacks32>(*this)),
250 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 257 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
251 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 258 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
252 jit(MakeJit(nullptr)) {} 259 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
253 260
254ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 261ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
255 262
256void ARM_Dynarmic_32::SetPC(u64 pc) { 263void ARM_Dynarmic_32::SetPC(u64 pc) {
257 jit->Regs()[15] = static_cast<u32>(pc); 264 jit.load()->Regs()[15] = static_cast<u32>(pc);
258} 265}
259 266
260u64 ARM_Dynarmic_32::GetPC() const { 267u64 ARM_Dynarmic_32::GetPC() const {
261 return jit->Regs()[15]; 268 return jit.load()->Regs()[15];
262} 269}
263 270
264u64 ARM_Dynarmic_32::GetReg(int index) const { 271u64 ARM_Dynarmic_32::GetReg(int index) const {
265 return jit->Regs()[index]; 272 return jit.load()->Regs()[index];
266} 273}
267 274
268void ARM_Dynarmic_32::SetReg(int index, u64 value) { 275void ARM_Dynarmic_32::SetReg(int index, u64 value) {
269 jit->Regs()[index] = static_cast<u32>(value); 276 jit.load()->Regs()[index] = static_cast<u32>(value);
270} 277}
271 278
272u128 ARM_Dynarmic_32::GetVectorReg(int index) const { 279u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -276,11 +283,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
276void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} 283void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
277 284
278u32 ARM_Dynarmic_32::GetPSTATE() const { 285u32 ARM_Dynarmic_32::GetPSTATE() const {
279 return jit->Cpsr(); 286 return jit.load()->Cpsr();
280} 287}
281 288
282void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { 289void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
283 jit->SetCpsr(cpsr); 290 jit.load()->SetCpsr(cpsr);
284} 291}
285 292
286u64 ARM_Dynarmic_32::GetTlsAddress() const { 293u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -301,7 +308,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
301 308
302void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 309void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
303 Dynarmic::A32::Context context; 310 Dynarmic::A32::Context context;
304 jit->SaveContext(context); 311 jit.load()->SaveContext(context);
305 ctx.cpu_registers = context.Regs(); 312 ctx.cpu_registers = context.Regs();
306 ctx.extension_registers = context.ExtRegs(); 313 ctx.extension_registers = context.ExtRegs();
307 ctx.cpsr = context.Cpsr(); 314 ctx.cpsr = context.Cpsr();
@@ -314,27 +321,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
314 context.ExtRegs() = ctx.extension_registers; 321 context.ExtRegs() = ctx.extension_registers;
315 context.SetCpsr(ctx.cpsr); 322 context.SetCpsr(ctx.cpsr);
316 context.SetFpscr(ctx.fpscr); 323 context.SetFpscr(ctx.fpscr);
317 jit->LoadContext(context); 324 jit.load()->LoadContext(context);
318} 325}
319 326
320void ARM_Dynarmic_32::PrepareReschedule() { 327void ARM_Dynarmic_32::PrepareReschedule() {
321 jit->HaltExecution(break_loop); 328 jit.load()->HaltExecution(break_loop);
322} 329}
323 330
324void ARM_Dynarmic_32::SignalInterrupt() { 331void ARM_Dynarmic_32::SignalInterrupt() {
325 jit->HaltExecution(break_loop); 332 jit.load()->HaltExecution(break_loop);
326} 333}
327 334
328void ARM_Dynarmic_32::ClearInstructionCache() { 335void ARM_Dynarmic_32::ClearInstructionCache() {
329 jit->ClearCache(); 336 jit.load()->ClearCache();
330} 337}
331 338
332void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { 339void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
333 jit->InvalidateCacheRange(static_cast<u32>(addr), size); 340 jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
334} 341}
335 342
336void ARM_Dynarmic_32::ClearExclusiveState() { 343void ARM_Dynarmic_32::ClearExclusiveState() {
337 jit->ClearExclusiveState(); 344 jit.load()->ClearExclusiveState();
338} 345}
339 346
340void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, 347void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -345,13 +352,14 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
345 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 352 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
346 auto iter = jit_cache.find(key); 353 auto iter = jit_cache.find(key);
347 if (iter != jit_cache.end()) { 354 if (iter != jit_cache.end()) {
348 jit = iter->second; 355 jit.store(iter->second.get());
349 LoadContext(ctx); 356 LoadContext(ctx);
350 return; 357 return;
351 } 358 }
352 jit = MakeJit(&page_table); 359 std::shared_ptr new_jit = MakeJit(&page_table);
360 jit.store(new_jit.get());
353 LoadContext(ctx); 361 LoadContext(ctx);
354 jit_cache.emplace(key, jit); 362 jit_cache.emplace(key, std::move(new_jit));
355} 363}
356 364
357} // namespace Core 365} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 3f68a4ff1..684937353 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -80,7 +81,11 @@ private:
80 std::shared_ptr<DynarmicCP15> cp15; 81 std::shared_ptr<DynarmicCP15> cp15;
81 std::size_t core_index; 82 std::size_t core_index;
82 DynarmicExclusiveMonitor& exclusive_monitor; 83 DynarmicExclusiveMonitor& exclusive_monitor;
83 std::shared_ptr<Dynarmic::A32::Jit> jit; 84
85 std::shared_ptr<Dynarmic::A32::Jit> null_jit;
86
87 // A raw pointer here is fine; we never delete Jit instances.
88 std::atomic<Dynarmic::A32::Jit*> jit;
84 89
85 // SVC callback 90 // SVC callback
86 u32 svc_swi{}; 91 u32 svc_swi{};
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 68822a1fc..ae0b158c2 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -109,7 +109,7 @@ public:
109 break; 109 break;
110 } 110 }
111 111
112 parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation); 112 parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
113 } 113 }
114 114
115 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 115 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -130,7 +130,7 @@ public:
130 130
131 void CallSVC(u32 swi) override { 131 void CallSVC(u32 swi) override {
132 parent.svc_swi = swi; 132 parent.svc_swi = swi;
133 parent.jit->HaltExecution(svc_call); 133 parent.jit.load()->HaltExecution(svc_call);
134 } 134 }
135 135
136 void AddTicks(u64 ticks) override { 136 void AddTicks(u64 ticks) override {
@@ -212,6 +212,13 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
212 config.code_cache_size = 512_MiB; 212 config.code_cache_size = 512_MiB;
213 config.far_code_offset = 400_MiB; 213 config.far_code_offset = 400_MiB;
214 214
215 // null_jit
216 if (!page_table) {
217 // Don't waste too much memory on null_jit
218 config.code_cache_size = 8_MiB;
219 config.far_code_offset = 4_MiB;
220 }
221
215 // Safe optimizations 222 // Safe optimizations
216 if (Settings::values.cpu_debug_mode) { 223 if (Settings::values.cpu_debug_mode) {
217 if (!Settings::values.cpuopt_page_tables) { 224 if (!Settings::values.cpuopt_page_tables) {
@@ -289,18 +296,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
289 296
290void ARM_Dynarmic_64::Run() { 297void ARM_Dynarmic_64::Run() {
291 while (true) { 298 while (true) {
292 const auto hr = jit->Run(); 299 const auto hr = jit.load()->Run();
293 if (Has(hr, svc_call)) { 300 if (Has(hr, svc_call)) {
294 Kernel::Svc::Call(system, svc_swi); 301 Kernel::Svc::Call(system, svc_swi);
295 } 302 }
296 if (Has(hr, break_loop)) { 303 if (Has(hr, break_loop) || !uses_wall_clock) {
297 break; 304 break;
298 } 305 }
299 } 306 }
300} 307}
301 308
302void ARM_Dynarmic_64::Step() { 309void ARM_Dynarmic_64::Step() {
303 jit->Step(); 310 jit.load()->Step();
304} 311}
305 312
306ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 313ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -309,40 +316,40 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handl
309 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, 316 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
310 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 317 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
311 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 318 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
312 jit(MakeJit(nullptr, 48)) {} 319 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
313 320
314ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 321ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
315 322
316void ARM_Dynarmic_64::SetPC(u64 pc) { 323void ARM_Dynarmic_64::SetPC(u64 pc) {
317 jit->SetPC(pc); 324 jit.load()->SetPC(pc);
318} 325}
319 326
320u64 ARM_Dynarmic_64::GetPC() const { 327u64 ARM_Dynarmic_64::GetPC() const {
321 return jit->GetPC(); 328 return jit.load()->GetPC();
322} 329}
323 330
324u64 ARM_Dynarmic_64::GetReg(int index) const { 331u64 ARM_Dynarmic_64::GetReg(int index) const {
325 return jit->GetRegister(index); 332 return jit.load()->GetRegister(index);
326} 333}
327 334
328void ARM_Dynarmic_64::SetReg(int index, u64 value) { 335void ARM_Dynarmic_64::SetReg(int index, u64 value) {
329 jit->SetRegister(index, value); 336 jit.load()->SetRegister(index, value);
330} 337}
331 338
332u128 ARM_Dynarmic_64::GetVectorReg(int index) const { 339u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
333 return jit->GetVector(index); 340 return jit.load()->GetVector(index);
334} 341}
335 342
336void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { 343void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
337 jit->SetVector(index, value); 344 jit.load()->SetVector(index, value);
338} 345}
339 346
340u32 ARM_Dynarmic_64::GetPSTATE() const { 347u32 ARM_Dynarmic_64::GetPSTATE() const {
341 return jit->GetPstate(); 348 return jit.load()->GetPstate();
342} 349}
343 350
344void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { 351void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
345 jit->SetPstate(pstate); 352 jit.load()->SetPstate(pstate);
346} 353}
347 354
348u64 ARM_Dynarmic_64::GetTlsAddress() const { 355u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -362,45 +369,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
362} 369}
363 370
364void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 371void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
365 ctx.cpu_registers = jit->GetRegisters(); 372 Dynarmic::A64::Jit* j = jit.load();
366 ctx.sp = jit->GetSP(); 373 ctx.cpu_registers = j->GetRegisters();
367 ctx.pc = jit->GetPC(); 374 ctx.sp = j->GetSP();
368 ctx.pstate = jit->GetPstate(); 375 ctx.pc = j->GetPC();
369 ctx.vector_registers = jit->GetVectors(); 376 ctx.pstate = j->GetPstate();
370 ctx.fpcr = jit->GetFpcr(); 377 ctx.vector_registers = j->GetVectors();
371 ctx.fpsr = jit->GetFpsr(); 378 ctx.fpcr = j->GetFpcr();
379 ctx.fpsr = j->GetFpsr();
372 ctx.tpidr = cb->tpidr_el0; 380 ctx.tpidr = cb->tpidr_el0;
373} 381}
374 382
375void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { 383void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
376 jit->SetRegisters(ctx.cpu_registers); 384 Dynarmic::A64::Jit* j = jit.load();
377 jit->SetSP(ctx.sp); 385 j->SetRegisters(ctx.cpu_registers);
378 jit->SetPC(ctx.pc); 386 j->SetSP(ctx.sp);
379 jit->SetPstate(ctx.pstate); 387 j->SetPC(ctx.pc);
380 jit->SetVectors(ctx.vector_registers); 388 j->SetPstate(ctx.pstate);
381 jit->SetFpcr(ctx.fpcr); 389 j->SetVectors(ctx.vector_registers);
382 jit->SetFpsr(ctx.fpsr); 390 j->SetFpcr(ctx.fpcr);
391 j->SetFpsr(ctx.fpsr);
383 SetTPIDR_EL0(ctx.tpidr); 392 SetTPIDR_EL0(ctx.tpidr);
384} 393}
385 394
386void ARM_Dynarmic_64::PrepareReschedule() { 395void ARM_Dynarmic_64::PrepareReschedule() {
387 jit->HaltExecution(break_loop); 396 jit.load()->HaltExecution(break_loop);
388} 397}
389 398
390void ARM_Dynarmic_64::SignalInterrupt() { 399void ARM_Dynarmic_64::SignalInterrupt() {
391 jit->HaltExecution(break_loop); 400 jit.load()->HaltExecution(break_loop);
392} 401}
393 402
394void ARM_Dynarmic_64::ClearInstructionCache() { 403void ARM_Dynarmic_64::ClearInstructionCache() {
395 jit->ClearCache(); 404 jit.load()->ClearCache();
396} 405}
397 406
398void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { 407void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
399 jit->InvalidateCacheRange(addr, size); 408 jit.load()->InvalidateCacheRange(addr, size);
400} 409}
401 410
402void ARM_Dynarmic_64::ClearExclusiveState() { 411void ARM_Dynarmic_64::ClearExclusiveState() {
403 jit->ClearExclusiveState(); 412 jit.load()->ClearExclusiveState();
404} 413}
405 414
406void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, 415void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -411,13 +420,14 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
411 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 420 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
412 auto iter = jit_cache.find(key); 421 auto iter = jit_cache.find(key);
413 if (iter != jit_cache.end()) { 422 if (iter != jit_cache.end()) {
414 jit = iter->second; 423 jit.store(iter->second.get());
415 LoadContext(ctx); 424 LoadContext(ctx);
416 return; 425 return;
417 } 426 }
418 jit = MakeJit(&page_table, new_address_space_size_in_bits); 427 std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
428 jit.store(new_jit.get());
419 LoadContext(ctx); 429 LoadContext(ctx);
420 jit_cache.emplace(key, jit); 430 jit_cache.emplace(key, std::move(new_jit));
421} 431}
422 432
423} // namespace Core 433} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 58bc7fbec..86018f196 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -74,7 +75,10 @@ private:
74 std::size_t core_index; 75 std::size_t core_index;
75 DynarmicExclusiveMonitor& exclusive_monitor; 76 DynarmicExclusiveMonitor& exclusive_monitor;
76 77
77 std::shared_ptr<Dynarmic::A64::Jit> jit; 78 std::shared_ptr<Dynarmic::A64::Jit> null_jit;
79
80 // A raw pointer here is fine; we never delete Jit instances.
81 std::atomic<Dynarmic::A64::Jit*> jit;
78 82
79 // SVC callback 83 // SVC callback
80 u32 svc_swi{}; 84 u32 svc_swi{};
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 888828fd0..28b63be43 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -8,13 +8,13 @@
8#include <chrono> 8#include <chrono>
9#include <functional> 9#include <functional>
10#include <memory> 10#include <memory>
11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
13#include <thread> 14#include <thread>
14#include <vector> 15#include <vector>
15 16
16#include "common/common_types.h" 17#include "common/common_types.h"
17#include "common/spin_lock.h"
18#include "common/thread.h" 18#include "common/thread.h"
19#include "common/wall_clock.h" 19#include "common/wall_clock.h"
20 20
@@ -149,8 +149,8 @@ private:
149 std::shared_ptr<EventType> ev_lost; 149 std::shared_ptr<EventType> ev_lost;
150 Common::Event event{}; 150 Common::Event event{};
151 Common::Event pause_event{}; 151 Common::Event pause_event{};
152 Common::SpinLock basic_lock{}; 152 std::mutex basic_lock;
153 Common::SpinLock advance_lock{}; 153 std::mutex advance_lock;
154 std::unique_ptr<std::thread> timer_thread; 154 std::unique_ptr<std::thread> timer_thread;
155 std::atomic<bool> paused{}; 155 std::atomic<bool> paused{};
156 std::atomic<bool> paused_set{}; 156 std::atomic<bool> paused_set{};
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index cc0dcd931..2f84d2b52 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -15,6 +15,7 @@ EmulatedDevices::EmulatedDevices() = default;
15EmulatedDevices::~EmulatedDevices() = default; 15EmulatedDevices::~EmulatedDevices() = default;
16 16
17void EmulatedDevices::ReloadFromSettings() { 17void EmulatedDevices::ReloadFromSettings() {
18 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 19 ReloadInput();
19} 20}
20 21
@@ -66,6 +67,8 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 67 key_index++;
67 } 68 }
68 69
70 ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
71
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 72 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) { 73 if (!mouse_button_devices[index]) {
71 continue; 74 continue;
@@ -120,6 +123,13 @@ void EmulatedDevices::ReloadInput() {
120 }, 123 },
121 }); 124 });
122 } 125 }
126
127 if (ring_analog_device) {
128 ring_analog_device->SetCallback({
129 .on_change =
130 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
131 });
132 }
123} 133}
124 134
125void EmulatedDevices::UnloadInput() { 135void EmulatedDevices::UnloadInput() {
@@ -155,6 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) { 165 if (!is_configuring) {
156 return; 166 return;
157 } 167 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
158} 169}
159 170
160void EmulatedDevices::RestoreConfig() { 171void EmulatedDevices::RestoreConfig() {
@@ -164,6 +175,15 @@ void EmulatedDevices::RestoreConfig() {
164 ReloadFromSettings(); 175 ReloadFromSettings();
165} 176}
166 177
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) { 188 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) { 189 if (index >= device_status.keyboard_values.size()) {
@@ -410,6 +430,23 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
410 TriggerOnChange(DeviceTriggerType::Mouse); 430 TriggerOnChange(DeviceTriggerType::Mouse);
411} 431}
412 432
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
413KeyboardValues EmulatedDevices::GetKeyboardValues() const { 450KeyboardValues EmulatedDevices::GetKeyboardValues() const {
414 std::scoped_lock lock{mutex}; 451 std::scoped_lock lock{mutex};
415 return device_status.keyboard_values; 452 return device_status.keyboard_values;
@@ -425,6 +462,10 @@ MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
425 return device_status.mouse_button_values; 462 return device_status.mouse_button_values;
426} 463}
427 464
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
428KeyboardKey EmulatedDevices::GetKeyboard() const { 469KeyboardKey EmulatedDevices::GetKeyboard() const {
429 std::scoped_lock lock{mutex}; 470 std::scoped_lock lock{mutex};
430 return device_status.keyboard_state; 471 return device_status.keyboard_state;
@@ -450,6 +491,10 @@ AnalogStickState EmulatedDevices::GetMouseWheel() const {
450 return device_status.mouse_wheel_state; 491 return device_status.mouse_wheel_state;
451} 492}
452 493
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
453void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
454 std::scoped_lock lock{callback_mutex}; 499 std::scoped_lock lock{callback_mutex};
455 for (const auto& poller_pair : callback_list) { 500 for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 73e9f0293..fb6451e7a 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,9 +26,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
29 30
30using MouseButtonParams = 31using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
32 34
33using KeyboardValues = 35using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +41,17 @@ using MouseButtonValues =
39using MouseAnalogValues = 41using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus; 43using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
42 45
43struct MousePosition { 46struct MousePosition {
44 f32 x; 47 f32 x;
45 f32 y; 48 f32 y;
46}; 49};
47 50
51struct RingSensorForce {
52 f32 force;
53};
54
48struct DeviceStatus { 55struct DeviceStatus {
49 // Data from input_common 56 // Data from input_common
50 KeyboardValues keyboard_values{}; 57 KeyboardValues keyboard_values{};
@@ -52,6 +59,7 @@ struct DeviceStatus {
52 MouseButtonValues mouse_button_values{}; 59 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{}; 60 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{}; 61 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
55 63
56 // Data for HID serices 64 // Data for HID serices
57 KeyboardKey keyboard_state{}; 65 KeyboardKey keyboard_state{};
@@ -59,12 +67,14 @@ struct DeviceStatus {
59 MouseButton mouse_button_state{}; 67 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{}; 68 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{}; 69 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
62}; 71};
63 72
64enum class DeviceTriggerType { 73enum class DeviceTriggerType {
65 Keyboard, 74 Keyboard,
66 KeyboardModdifier, 75 KeyboardModdifier,
67 Mouse, 76 Mouse,
77 RingController,
68}; 78};
69 79
70struct InterfaceUpdateCallback { 80struct InterfaceUpdateCallback {
@@ -110,6 +120,15 @@ public:
110 /// Reverts any mapped changes made that weren't saved 120 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig(); 121 void RestoreConfig();
112 122
123 // Returns the current mapped ring device
124 Common::ParamPackage GetRingParam() const;
125
126 /**
127 * Updates the current mapped ring device
128 * @param param ParamPackage with ring sensor data to be mapped
129 */
130 void SetRingParam(Common::ParamPackage param);
131
113 /// Returns the latest status of button input from the keyboard with parameters 132 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const; 133 KeyboardValues GetKeyboardValues() const;
115 134
@@ -119,6 +138,9 @@ public:
119 /// Returns the latest status of button input from the mouse with parameters 138 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const; 139 MouseButtonValues GetMouseButtonsValues() const;
121 140
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
122 /// Returns the latest status of button input from the keyboard 144 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const; 145 KeyboardKey GetKeyboard() const;
124 146
@@ -134,6 +156,9 @@ public:
134 /// Returns the latest mouse wheel change 156 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const; 157 AnalogStickState GetMouseWheel() const;
136 158
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
137 /** 162 /**
138 * Adds a callback to the list of events 163 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered 164 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +211,12 @@ private:
186 void SetMouseStick(const Common::Input::CallbackStatus& callback); 211 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187 212
188 /** 213 /**
214 * Updates the ring analog sensor status of the ring controller
215 * @param callback A CallbackStatus containing the force status
216 */
217 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
218
219 /**
189 * Triggers a callback that something has changed on the device status 220 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger 221 * @param type Input type of the event to trigger
191 */ 222 */
@@ -193,11 +224,14 @@ private:
193 224
194 bool is_configuring{false}; 225 bool is_configuring{false};
195 226
227 RingAnalogParams ring_params;
228
196 KeyboardDevices keyboard_devices; 229 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices; 230 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices; 231 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices; 232 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device; 233 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
201 235
202 mutable std::mutex mutex; 236 mutable std::mutex mutex;
203 mutable std::mutex callback_mutex; 237 mutable std::mutex callback_mutex;
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 6f44b534f..47425a3a1 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -8,7 +8,6 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/spin_lock.h"
12#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
13#include "core/hle/kernel/k_priority_queue.h" 12#include "core/hle/kernel/k_priority_queue.h"
14#include "core/hle/kernel/k_scheduler_lock.h" 13#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +79,7 @@ private:
80 79
81 /// Lists all thread ids that aren't deleted/etc. 80 /// Lists all thread ids that aren't deleted/etc.
82 std::vector<KThread*> thread_list; 81 std::vector<KThread*> thread_list;
83 Common::SpinLock global_list_guard{}; 82 std::mutex global_list_guard;
84}; 83};
85 84
86} // namespace Kernel 85} // namespace Kernel
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 63bbe02e9..09eaf004c 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -35,9 +35,14 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
35 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
36 36
37 // Clear the memory. 37 // Clear the memory.
38 for (const auto& block : m_page_group.Nodes()) { 38 //
39 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); 39 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
40 } 40 // guest memory, and is not specifically required if the guest program is correctly
41 // written, so disable until this is further investigated.
42 //
43 // for (const auto& block : m_page_group.Nodes()) {
44 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
45 // }
41 46
42 // Set remaining tracking members. 47 // Set remaining tracking members.
43 m_address = addr; 48 m_address = addr;
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index 6c0bb1672..526eb4b70 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -705,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
705 prev_thread = nullptr; 705 prev_thread = nullptr;
706 } 706 }
707 707
708 thread->context_guard.Unlock(); 708 thread->context_guard.unlock();
709} 709}
710 710
711void KScheduler::Reload(KThread* thread) { 711void KScheduler::Reload(KThread* thread) {
@@ -794,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
794 do { 794 do {
795 auto next_thread = current_thread.load(); 795 auto next_thread = current_thread.load();
796 if (next_thread != nullptr) { 796 if (next_thread != nullptr) {
797 const auto locked = next_thread->context_guard.TryLock(); 797 const auto locked = next_thread->context_guard.try_lock();
798 if (state.needs_scheduling.load()) { 798 if (state.needs_scheduling.load()) {
799 next_thread->context_guard.Unlock(); 799 next_thread->context_guard.unlock();
800 break; 800 break;
801 } 801 }
802 if (next_thread->GetActiveCore() != core_id) { 802 if (next_thread->GetActiveCore() != core_id) {
803 next_thread->context_guard.Unlock(); 803 next_thread->context_guard.unlock();
804 break; 804 break;
805 } 805 }
806 if (!locked) { 806 if (!locked) {
diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 4412aa4bb..527ff0f9f 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -4,51 +4,18 @@
4 4
5#include "core/hle/kernel/k_spin_lock.h" 5#include "core/hle/kernel/k_spin_lock.h"
6 6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // namespace
34
35namespace Kernel { 7namespace Kernel {
36 8
37void KSpinLock::Lock() { 9void KSpinLock::Lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) { 10 lck.lock();
39 ThreadPause();
40 }
41} 11}
42 12
43void KSpinLock::Unlock() { 13void KSpinLock::Unlock() {
44 lck.clear(std::memory_order_release); 14 lck.unlock();
45} 15}
46 16
47bool KSpinLock::TryLock() { 17bool KSpinLock::TryLock() {
48 if (lck.test_and_set(std::memory_order_acquire)) { 18 return lck.try_lock();
49 return false;
50 }
51 return true;
52} 19}
53 20
54} // namespace Kernel 21} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 4d87d006a..7868b25a5 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <mutex>
8 8
9#include "core/hle/kernel/k_scoped_lock.h" 9#include "core/hle/kernel/k_scoped_lock.h"
10 10
@@ -25,7 +25,7 @@ public:
25 [[nodiscard]] bool TryLock(); 25 [[nodiscard]] bool TryLock();
26 26
27private: 27private:
28 std::atomic_flag lck = ATOMIC_FLAG_INIT; 28 std::mutex lck;
29}; 29};
30 30
31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. 31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index d3bb1c871..af71987e8 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -723,10 +723,10 @@ void KThread::UpdateState() {
723 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 723 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
724 724
725 // Set our suspend flags in state. 725 // Set our suspend flags in state.
726 const ThreadState old_state = thread_state; 726 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
727 const auto new_state = 727 const auto new_state =
728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); 728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
729 thread_state = new_state; 729 thread_state.store(new_state, std::memory_order_relaxed);
730 730
731 // Note the state change in scheduler. 731 // Note the state change in scheduler.
732 if (new_state != old_state) { 732 if (new_state != old_state) {
@@ -738,8 +738,8 @@ void KThread::Continue() {
738 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 738 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
739 739
740 // Clear our suspend flags in state. 740 // Clear our suspend flags in state.
741 const ThreadState old_state = thread_state; 741 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
742 thread_state = old_state & ThreadState::Mask; 742 thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
743 743
744 // Note the state change in scheduler. 744 // Note the state change in scheduler.
745 KScheduler::OnThreadStateChanged(kernel, this, old_state); 745 KScheduler::OnThreadStateChanged(kernel, this, old_state);
@@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
1079 return; 1079 return;
1080 } 1080 }
1081 1081
1082 // Block until we can grab the lock. 1082 // Block until we are no longer waiting.
1083 KScopedSpinLock lk{dummy_wait_lock}; 1083 std::unique_lock lk(dummy_wait_lock);
1084} 1084 dummy_wait_cv.wait(
1085 1085 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1086void KThread::IfDummyThreadBeginWait() {
1087 if (!IsDummyThread()) {
1088 return;
1089 }
1090
1091 // Ensure the thread will block when IfDummyThreadTryWait is called.
1092 dummy_wait_lock.Lock();
1093} 1086}
1094 1087
1095void KThread::IfDummyThreadEndWait() { 1088void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
1097 return; 1090 return;
1098 } 1091 }
1099 1092
1100 // Ensure the thread will no longer block. 1093 // Wake up the waiting thread.
1101 dummy_wait_lock.Unlock(); 1094 dummy_wait_cv.notify_one();
1102} 1095}
1103 1096
1104void KThread::BeginWait(KThreadQueue* queue) { 1097void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
1107 1100
1108 // Set our wait queue. 1101 // Set our wait queue.
1109 wait_queue = queue; 1102 wait_queue = queue;
1110
1111 // Special case for dummy threads to ensure they block.
1112 IfDummyThreadBeginWait();
1113} 1103}
1114 1104
1115void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { 1105void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
1158 SetMutexWaitAddressForDebugging({}); 1148 SetMutexWaitAddressForDebugging({});
1159 SetWaitReasonForDebugging({}); 1149 SetWaitReasonForDebugging({});
1160 1150
1161 const ThreadState old_state = thread_state; 1151 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
1162 thread_state = 1152 thread_state.store(
1163 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); 1153 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
1164 if (thread_state != old_state) { 1154 std::memory_order_relaxed);
1155 if (thread_state.load(std::memory_order_relaxed) != old_state) {
1165 KScheduler::OnThreadStateChanged(kernel, this, old_state); 1156 KScheduler::OnThreadStateChanged(kernel, this, old_state);
1166 } 1157 }
1167} 1158}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d0fd85130..4892fdf76 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -6,6 +6,8 @@
6 6
7#include <array> 7#include <array>
8#include <atomic> 8#include <atomic>
9#include <condition_variable>
10#include <mutex>
9#include <span> 11#include <span>
10#include <string> 12#include <string>
11#include <utility> 13#include <utility>
@@ -15,6 +17,7 @@
15 17
16#include "common/common_types.h" 18#include "common/common_types.h"
17#include "common/intrusive_red_black_tree.h" 19#include "common/intrusive_red_black_tree.h"
20#include "common/spin_lock.h"
18#include "core/arm/arm_interface.h" 21#include "core/arm/arm_interface.h"
19#include "core/hle/kernel/k_affinity_mask.h" 22#include "core/hle/kernel/k_affinity_mask.h"
20#include "core/hle/kernel/k_light_lock.h" 23#include "core/hle/kernel/k_light_lock.h"
@@ -256,11 +259,11 @@ public:
256 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); 259 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
257 260
258 [[nodiscard]] ThreadState GetState() const { 261 [[nodiscard]] ThreadState GetState() const {
259 return thread_state & ThreadState::Mask; 262 return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
260 } 263 }
261 264
262 [[nodiscard]] ThreadState GetRawState() const { 265 [[nodiscard]] ThreadState GetRawState() const {
263 return thread_state; 266 return thread_state.load(std::memory_order_relaxed);
264 } 267 }
265 268
266 void SetState(ThreadState state); 269 void SetState(ThreadState state);
@@ -642,7 +645,6 @@ public:
642 // blocking as needed. 645 // blocking as needed.
643 646
644 void IfDummyThreadTryWait(); 647 void IfDummyThreadTryWait();
645 void IfDummyThreadBeginWait();
646 void IfDummyThreadEndWait(); 648 void IfDummyThreadEndWait();
647 649
648private: 650private:
@@ -762,13 +764,14 @@ private:
762 s8 priority_inheritance_count{}; 764 s8 priority_inheritance_count{};
763 bool resource_limit_release_hint{}; 765 bool resource_limit_release_hint{};
764 StackParameters stack_parameters{}; 766 StackParameters stack_parameters{};
765 KSpinLock context_guard{}; 767 Common::SpinLock context_guard{};
766 KSpinLock dummy_wait_lock{};
767 768
768 // For emulation 769 // For emulation
769 std::shared_ptr<Common::Fiber> host_context{}; 770 std::shared_ptr<Common::Fiber> host_context{};
770 bool is_single_core{}; 771 bool is_single_core{};
771 ThreadType thread_type{}; 772 ThreadType thread_type{};
773 std::mutex dummy_wait_lock;
774 std::condition_variable dummy_wait_cv;
772 775
773 // For debugging 776 // For debugging
774 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 777 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index d840d44e6..5984afd7e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -140,6 +140,7 @@ struct KernelCore::Impl {
140 CleanupObject(font_shared_mem); 140 CleanupObject(font_shared_mem);
141 CleanupObject(irs_shared_mem); 141 CleanupObject(irs_shared_mem);
142 CleanupObject(time_shared_mem); 142 CleanupObject(time_shared_mem);
143 CleanupObject(hidbus_shared_mem);
143 CleanupObject(system_resource_limit); 144 CleanupObject(system_resource_limit);
144 145
145 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 146 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -622,16 +623,20 @@ struct KernelCore::Impl {
622 constexpr std::size_t font_size{0x1100000}; 623 constexpr std::size_t font_size{0x1100000};
623 constexpr std::size_t irs_size{0x8000}; 624 constexpr std::size_t irs_size{0x8000};
624 constexpr std::size_t time_size{0x1000}; 625 constexpr std::size_t time_size{0x1000};
626 constexpr std::size_t hidbus_size{0x1000};
625 627
626 const PAddr hid_phys_addr{system_pool.GetAddress()}; 628 const PAddr hid_phys_addr{system_pool.GetAddress()};
627 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; 629 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
628 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; 630 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
629 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; 631 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
632 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
633 time_size};
630 634
631 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 635 hid_shared_mem = KSharedMemory::Create(system.Kernel());
632 font_shared_mem = KSharedMemory::Create(system.Kernel()); 636 font_shared_mem = KSharedMemory::Create(system.Kernel());
633 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 637 irs_shared_mem = KSharedMemory::Create(system.Kernel());
634 time_shared_mem = KSharedMemory::Create(system.Kernel()); 638 time_shared_mem = KSharedMemory::Create(system.Kernel());
639 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
635 640
636 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 641 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
637 {hid_phys_addr, hid_size / PageSize}, 642 {hid_phys_addr, hid_size / PageSize},
@@ -649,6 +654,10 @@ struct KernelCore::Impl {
649 {time_phys_addr, time_size / PageSize}, 654 {time_phys_addr, time_size / PageSize},
650 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 655 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
651 time_phys_addr, time_size, "Time:SharedMemory"); 656 time_phys_addr, time_size, "Time:SharedMemory");
657 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
658 {hidbus_phys_addr, hidbus_size / PageSize},
659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
660 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
652 } 661 }
653 662
654 KClientPort* CreateNamedServicePort(std::string name) { 663 KClientPort* CreateNamedServicePort(std::string name) {
@@ -748,6 +757,7 @@ struct KernelCore::Impl {
748 Kernel::KSharedMemory* font_shared_mem{}; 757 Kernel::KSharedMemory* font_shared_mem{};
749 Kernel::KSharedMemory* irs_shared_mem{}; 758 Kernel::KSharedMemory* irs_shared_mem{};
750 Kernel::KSharedMemory* time_shared_mem{}; 759 Kernel::KSharedMemory* time_shared_mem{};
760 Kernel::KSharedMemory* hidbus_shared_mem{};
751 761
752 // Memory layout 762 // Memory layout
753 std::unique_ptr<KMemoryLayout> memory_layout; 763 std::unique_ptr<KMemoryLayout> memory_layout;
@@ -1047,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
1047 return *impl->time_shared_mem; 1057 return *impl->time_shared_mem;
1048} 1058}
1049 1059
1060Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
1061 return *impl->hidbus_shared_mem;
1062}
1063
1064const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1065 return *impl->hidbus_shared_mem;
1066}
1067
1050void KernelCore::Suspend(bool in_suspention) { 1068void KernelCore::Suspend(bool in_suspention) {
1051 const bool should_suspend = exception_exited || in_suspention; 1069 const bool should_suspend = exception_exited || in_suspention;
1052 { 1070 {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d709c368b..12e44b8a5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -264,6 +264,12 @@ public:
264 /// Gets the shared memory object for Time services. 264 /// Gets the shared memory object for Time services.
265 const Kernel::KSharedMemory& GetTimeSharedMem() const; 265 const Kernel::KSharedMemory& GetTimeSharedMem() const;
266 266
267 /// Gets the shared memory object for HIDBus services.
268 Kernel::KSharedMemory& GetHidBusSharedMem();
269
270 /// Gets the shared memory object for HIDBus services.
271 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
272
267 /// Suspend/unsuspend the OS. 273 /// Suspend/unsuspend the OS.
268 void Suspend(bool in_suspention); 274 void Suspend(bool in_suspention);
269 275
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 18a5f40f8..cc49e8c7e 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/spin_lock.h"
6#include "core/arm/cpu_interrupt_handler.h" 5#include "core/arm/cpu_interrupt_handler.h"
7#include "core/arm/dynarmic/arm_dynarmic_32.h" 6#include "core/arm/dynarmic/arm_dynarmic_32.h"
8#include "core/arm/dynarmic/arm_dynarmic_64.h" 7#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -16,7 +15,7 @@ namespace Kernel {
16PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 15PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
17 Core::CPUInterrupts& interrupts_) 16 Core::CPUInterrupts& interrupts_)
18 : core_index{core_index_}, system{system_}, scheduler{scheduler_}, 17 : core_index{core_index_}, system{system_}, scheduler{scheduler_},
19 interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { 18 interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
20#ifdef ARCHITECTURE_x86_64 19#ifdef ARCHITECTURE_x86_64
21 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 20 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
22 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 21 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 16a032e89..f2112fc1d 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,13 +6,10 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9#include <mutex>
9 10
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11 12
12namespace Common {
13class SpinLock;
14}
15
16namespace Kernel { 13namespace Kernel {
17class KScheduler; 14class KScheduler;
18} // namespace Kernel 15} // namespace Kernel
@@ -91,7 +88,7 @@ private:
91 Core::System& system; 88 Core::System& system;
92 Kernel::KScheduler& scheduler; 89 Kernel::KScheduler& scheduler;
93 Core::CPUInterrupts& interrupts; 90 Core::CPUInterrupts& interrupts;
94 std::unique_ptr<Common::SpinLock> guard; 91 std::unique_ptr<std::mutex> guard;
95 std::unique_ptr<Core::ARM_Interface> arm_interface; 92 std::unique_ptr<Core::ARM_Interface> arm_interface;
96}; 93};
97 94
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b2cec2253..9d3e0a658 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/service/hid/errors.h" 17#include "core/hle/service/hid/errors.h"
18#include "core/hle/service/hid/hid.h" 18#include "core/hle/service/hid/hid.h"
19#include "core/hle/service/hid/hidbus.h"
19#include "core/hle/service/hid/irs.h" 20#include "core/hle/service/hid/irs.h"
20#include "core/hle/service/hid/xcd.h" 21#include "core/hle/service/hid/xcd.h"
21#include "core/memory.h" 22#include "core/memory.h"
@@ -2128,32 +2129,6 @@ public:
2128 } 2129 }
2129}; 2130};
2130 2131
2131class HidBus final : public ServiceFramework<HidBus> {
2132public:
2133 explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
2134 // clang-format off
2135 static const FunctionInfo functions[] = {
2136 {1, nullptr, "GetBusHandle"},
2137 {2, nullptr, "IsExternalDeviceConnected"},
2138 {3, nullptr, "Initialize"},
2139 {4, nullptr, "Finalize"},
2140 {5, nullptr, "EnableExternalDevice"},
2141 {6, nullptr, "GetExternalDeviceId"},
2142 {7, nullptr, "SendCommandAsync"},
2143 {8, nullptr, "GetSendCommandAsynceResult"},
2144 {9, nullptr, "SetEventForSendCommandAsycResult"},
2145 {10, nullptr, "GetSharedMemoryHandle"},
2146 {11, nullptr, "EnableJoyPollingReceiveMode"},
2147 {12, nullptr, "DisableJoyPollingReceiveMode"},
2148 {13, nullptr, "GetPollingData"},
2149 {14, nullptr, "SetStatusManagerType"},
2150 };
2151 // clang-format on
2152
2153 RegisterHandlers(functions);
2154 }
2155};
2156
2157void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2132void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2158 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2133 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2159 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2134 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..af7662a15
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,531 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
10#include "core/hid/hid_types.h"
11#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h"
15#include "core/hle/kernel/k_transfer_memory.h"
16#include "core/hle/service/hid/hidbus.h"
17#include "core/hle/service/hid/hidbus/ringcon.h"
18#include "core/hle/service/hid/hidbus/starlink.h"
19#include "core/hle/service/hid/hidbus/stubbed.h"
20#include "core/hle/service/service.h"
21#include "core/memory.h"
22
23namespace Service::HID {
24// (15ms, 66Hz)
25constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
26
27HidBus::HidBus(Core::System& system_)
28 : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
29
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {1, &HidBus::GetBusHandle, "GetBusHandle"},
33 {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
34 {3, &HidBus::Initialize, "Initialize"},
35 {4, &HidBus::Finalize, "Finalize"},
36 {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
37 {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
38 {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
39 {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
40 {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
41 {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
42 {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
43 {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
44 {13, nullptr, "GetPollingData"},
45 {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
46 };
47 // clang-format on
48
49 RegisterHandlers(functions);
50
51 // Register update callbacks
52 hidbus_update_event = Core::Timing::CreateEvent(
53 "Hidbus::UpdateCallback",
54 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
55 const auto guard = LockService();
56 UpdateHidbus(user_data, ns_late);
57 });
58
59 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
60}
61
62HidBus::~HidBus() {
63 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
64}
65
66void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
67 auto& core_timing = system.CoreTiming();
68
69 if (is_hidbus_enabled) {
70 for (std::size_t i = 0; i < devices.size(); ++i) {
71 if (!devices[i].is_device_initializated) {
72 continue;
73 }
74 auto& device = devices[i].device;
75 device->OnUpdate();
76 auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
77 cur_entry.is_polling_mode = device->IsPollingMode();
78 cur_entry.polling_mode = device->GetPollingMode();
79 cur_entry.is_enabled = device->IsEnabled();
80
81 u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
82 std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
83 sizeof(HidbusStatusManagerEntry));
84 }
85 }
86
87 // If ns_late is higher than the update rate ignore the delay
88 if (ns_late > hidbus_update_ns) {
89 ns_late = {};
90 }
91
92 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
93}
94
95std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
96 for (std::size_t i = 0; i < devices.size(); ++i) {
97 const auto& device_handle = devices[i].handle;
98 if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
99 handle.internal_index == device_handle.internal_index &&
100 handle.player_number == device_handle.player_number &&
101 handle.bus_type == device_handle.bus_type &&
102 handle.is_valid == device_handle.is_valid) {
103 return i;
104 }
105 }
106 return std::nullopt;
107}
108
109void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
110 IPC::RequestParser rp{ctx};
111 struct Parameters {
112 Core::HID::NpadIdType npad_id;
113 INSERT_PADDING_WORDS_NOINIT(1);
114 BusType bus_type;
115 u64 applet_resource_user_id;
116 };
117 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
118
119 const auto parameters{rp.PopRaw<Parameters>()};
120
121 LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
122 parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
123
124 bool is_handle_found = 0;
125 std::size_t handle_index = 0;
126
127 for (std::size_t i = 0; i < devices.size(); i++) {
128 const auto& handle = devices[i].handle;
129 if (!handle.is_valid) {
130 continue;
131 }
132 if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
133 handle.bus_type == parameters.bus_type) {
134 is_handle_found = true;
135 handle_index = i;
136 break;
137 }
138 }
139
140 // Handle not found. Create a new one
141 if (!is_handle_found) {
142 for (std::size_t i = 0; i < devices.size(); i++) {
143 if (devices[i].handle.is_valid) {
144 continue;
145 }
146 devices[i].handle = {
147 .abstracted_pad_id = static_cast<u8>(i),
148 .internal_index = static_cast<u8>(i),
149 .player_number = static_cast<u8>(parameters.npad_id),
150 .bus_type = parameters.bus_type,
151 .is_valid = true,
152 };
153 handle_index = i;
154 break;
155 }
156 }
157
158 struct OutData {
159 bool is_valid;
160 INSERT_PADDING_BYTES(7);
161 BusHandle handle;
162 };
163 static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
164
165 const OutData out_data{
166 .is_valid = true,
167 .handle = devices[handle_index].handle,
168 };
169
170 IPC::ResponseBuilder rb{ctx, 6};
171 rb.Push(ResultSuccess);
172 rb.PushRaw(out_data);
173}
174
175void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx};
177 const auto bus_handle_{rp.PopRaw<BusHandle>()};
178
179 LOG_INFO(Service_HID,
180 "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
181 "player_number={}, is_valid={}",
182 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
183 bus_handle_.player_number, bus_handle_.is_valid);
184
185 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
186
187 if (device_index) {
188 const auto& device = devices[device_index.value()].device;
189 const bool is_attached = device->IsDeviceActivated();
190
191 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess);
193 rb.Push(is_attached);
194 return;
195 }
196
197 LOG_ERROR(Service_HID, "Invalid handle");
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ResultUnknown);
200 return;
201}
202
203void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto bus_handle_{rp.PopRaw<BusHandle>()};
206 const auto applet_resource_user_id{rp.Pop<u64>()};
207
208 LOG_INFO(Service_HID,
209 "called, abstracted_pad_id={} bus_type={} internal_index={} "
210 "player_number={} is_valid={}, applet_resource_user_id={}",
211 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
212 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
213
214 is_hidbus_enabled = true;
215
216 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
217
218 if (device_index) {
219 const auto entry_index = devices[device_index.value()].handle.internal_index;
220 auto& cur_entry = hidbus_status.entries[entry_index];
221
222 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
223 MakeDevice<RingController>(bus_handle_);
224 devices[device_index.value()].is_device_initializated = true;
225 devices[device_index.value()].device->ActivateDevice();
226 cur_entry.is_in_focus = true;
227 cur_entry.is_connected = true;
228 cur_entry.is_connected_result = ResultSuccess;
229 cur_entry.is_enabled = false;
230 cur_entry.is_polling_mode = false;
231 } else {
232 MakeDevice<HidbusStubbed>(bus_handle_);
233 devices[device_index.value()].is_device_initializated = true;
234 cur_entry.is_in_focus = true;
235 cur_entry.is_connected = false;
236 cur_entry.is_connected_result = ResultSuccess;
237 cur_entry.is_enabled = false;
238 cur_entry.is_polling_mode = false;
239 }
240
241 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
242 sizeof(hidbus_status));
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ResultSuccess);
246 return;
247 }
248
249 LOG_ERROR(Service_HID, "Invalid handle");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ResultUnknown);
252 return;
253}
254
255void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto bus_handle_{rp.PopRaw<BusHandle>()};
258 const auto applet_resource_user_id{rp.Pop<u64>()};
259
260 LOG_INFO(Service_HID,
261 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
262 "player_number={}, is_valid={}, applet_resource_user_id={}",
263 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
264 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
265
266 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
267
268 if (device_index) {
269 const auto entry_index = devices[device_index.value()].handle.internal_index;
270 auto& cur_entry = hidbus_status.entries[entry_index];
271 auto& device = devices[device_index.value()].device;
272 devices[device_index.value()].is_device_initializated = false;
273 device->DeactivateDevice();
274
275 cur_entry.is_in_focus = true;
276 cur_entry.is_connected = false;
277 cur_entry.is_connected_result = ResultSuccess;
278 cur_entry.is_enabled = false;
279 cur_entry.is_polling_mode = false;
280 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
281 sizeof(hidbus_status));
282
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(ResultSuccess);
285 return;
286 }
287
288 LOG_ERROR(Service_HID, "Invalid handle");
289 IPC::ResponseBuilder rb{ctx, 2};
290 rb.Push(ResultUnknown);
291 return;
292}
293
294void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 struct Parameters {
297 bool enable;
298 INSERT_PADDING_BYTES_NOINIT(7);
299 BusHandle bus_handle;
300 u64 inval;
301 u64 applet_resource_user_id;
302 };
303 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
304
305 const auto parameters{rp.PopRaw<Parameters>()};
306
307 LOG_INFO(Service_HID,
308 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
309 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
310 parameters.enable, parameters.bus_handle.abstracted_pad_id,
311 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
312 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
313 parameters.applet_resource_user_id);
314
315 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
316
317 if (device_index) {
318 auto& device = devices[device_index.value()].device;
319 device->Enable(parameters.enable);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323 return;
324 }
325
326 LOG_ERROR(Service_HID, "Invalid handle");
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ResultUnknown);
329 return;
330}
331
332void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto bus_handle_{rp.PopRaw<BusHandle>()};
335
336 LOG_INFO(Service_HID,
337 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
338 "is_valid={}",
339 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
340 bus_handle_.player_number, bus_handle_.is_valid);
341
342 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
343
344 if (device_index) {
345 const auto& device = devices[device_index.value()].device;
346 u32 device_id = device->GetDeviceId();
347 IPC::ResponseBuilder rb{ctx, 3};
348 rb.Push(ResultSuccess);
349 rb.Push<u32>(device_id);
350 return;
351 }
352
353 LOG_ERROR(Service_HID, "Invalid handle");
354 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultUnknown);
356 return;
357}
358
359void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto data = ctx.ReadBuffer();
362 const auto bus_handle_{rp.PopRaw<BusHandle>()};
363
364 LOG_DEBUG(Service_HID,
365 "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
366 "player_number={}, is_valid={}",
367 data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
368 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
369
370 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
371
372 if (device_index) {
373 auto& device = devices[device_index.value()].device;
374 device->SetCommand(data);
375
376 IPC::ResponseBuilder rb{ctx, 2};
377 rb.Push(ResultSuccess);
378 return;
379 }
380
381 LOG_ERROR(Service_HID, "Invalid handle");
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ResultUnknown);
384 return;
385};
386
387void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto bus_handle_{rp.PopRaw<BusHandle>()};
390
391 LOG_DEBUG(Service_HID,
392 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
393 "is_valid={}",
394 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
395 bus_handle_.player_number, bus_handle_.is_valid);
396
397 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
398
399 if (device_index) {
400 const auto& device = devices[device_index.value()].device;
401 const std::vector<u8> data = device->GetReply();
402 const u64 data_size = ctx.WriteBuffer(data);
403
404 IPC::ResponseBuilder rb{ctx, 4};
405 rb.Push(ResultSuccess);
406 rb.Push<u64>(data_size);
407 return;
408 }
409
410 LOG_ERROR(Service_HID, "Invalid handle");
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ResultUnknown);
413 return;
414};
415
416void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
417 IPC::RequestParser rp{ctx};
418 const auto bus_handle_{rp.PopRaw<BusHandle>()};
419
420 LOG_INFO(Service_HID,
421 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
422 "is_valid={}",
423 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
424 bus_handle_.player_number, bus_handle_.is_valid);
425
426 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
427
428 if (device_index) {
429 const auto& device = devices[device_index.value()].device;
430 IPC::ResponseBuilder rb{ctx, 2, 1};
431 rb.Push(ResultSuccess);
432 rb.PushCopyObjects(device->GetSendCommandAsycEvent());
433 return;
434 }
435
436 LOG_ERROR(Service_HID, "Invalid handle");
437 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(ResultUnknown);
439 return;
440};
441
442void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
443 LOG_DEBUG(Service_HID, "called");
444
445 IPC::ResponseBuilder rb{ctx, 2, 1};
446 rb.Push(ResultSuccess);
447 rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
448}
449
450void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto t_mem_size{rp.Pop<u32>()};
453 const auto t_mem_handle{ctx.GetCopyHandle(0)};
454 const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
455 const auto bus_handle_{rp.PopRaw<BusHandle>()};
456
457 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
458
459 auto t_mem =
460 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
461
462 if (t_mem.IsNull()) {
463 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
464 IPC::ResponseBuilder rb{ctx, 2};
465 rb.Push(ResultUnknown);
466 return;
467 }
468
469 ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
470
471 LOG_INFO(Service_HID,
472 "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
473 "internal_index={}, player_number={}, is_valid={}",
474 t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
475 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
476
477 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
478
479 if (device_index) {
480 auto& device = devices[device_index.value()].device;
481 device->SetPollingMode(polling_mode_);
482 device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
483
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(ResultSuccess);
486 return;
487 }
488
489 LOG_ERROR(Service_HID, "Invalid handle");
490 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(ResultUnknown);
492 return;
493}
494
495void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
496 IPC::RequestParser rp{ctx};
497 const auto bus_handle_{rp.PopRaw<BusHandle>()};
498
499 LOG_INFO(Service_HID,
500 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
501 "is_valid={}",
502 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
503 bus_handle_.player_number, bus_handle_.is_valid);
504
505 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
506
507 if (device_index) {
508 auto& device = devices[device_index.value()].device;
509 device->DisablePollingMode();
510
511 IPC::ResponseBuilder rb{ctx, 2};
512 rb.Push(ResultSuccess);
513 return;
514 }
515
516 LOG_ERROR(Service_HID, "Invalid handle");
517 IPC::ResponseBuilder rb{ctx, 2};
518 rb.Push(ResultUnknown);
519 return;
520}
521
522void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
523 IPC::RequestParser rp{ctx};
524 const auto manager_type{rp.PopEnum<StatusManagerType>()};
525
526 LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
527
528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(ResultSuccess);
530};
531} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
new file mode 100644
index 000000000..b10d5156a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,131 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Core {
18class System;
19} // namespace Core
20
21namespace Service::HID {
22
23class HidBus final : public ServiceFramework<HidBus> {
24public:
25 explicit HidBus(Core::System& system_);
26 ~HidBus() override;
27
28private:
29 static const std::size_t max_number_of_handles = 0x13;
30
31 enum class HidBusDeviceId : std::size_t {
32 RingController = 0x20,
33 FamicomRight = 0x21,
34 Starlink = 0x28,
35 };
36
37 // This is nn::hidbus::detail::StatusManagerType
38 enum class StatusManagerType : u32 {
39 None,
40 Type16,
41 Type32,
42 };
43
44 // This is nn::hidbus::BusType
45 enum class BusType : u8 {
46 LeftJoyRail,
47 RightJoyRail,
48 InternalBus, // Lark microphone
49
50 MaxBusType,
51 };
52
53 // This is nn::hidbus::BusHandle
54 struct BusHandle {
55 u32 abstracted_pad_id;
56 u8 internal_index;
57 u8 player_number;
58 BusType bus_type;
59 bool is_valid;
60 };
61 static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
62
63 // This is nn::hidbus::JoyPollingReceivedData
64 struct JoyPollingReceivedData {
65 std::array<u8, 0x30> data;
66 u64 out_size;
67 u64 sampling_number;
68 };
69 static_assert(sizeof(JoyPollingReceivedData) == 0x40,
70 "JoyPollingReceivedData is an invalid size");
71
72 struct HidbusStatusManagerEntry {
73 u8 is_connected{};
74 INSERT_PADDING_BYTES(0x3);
75 ResultCode is_connected_result{0};
76 u8 is_enabled{};
77 u8 is_in_focus{};
78 u8 is_polling_mode{};
79 u8 reserved{};
80 JoyPollingMode polling_mode{};
81 INSERT_PADDING_BYTES(0x70); // Unknown
82 };
83 static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
84 "HidbusStatusManagerEntry is an invalid size");
85
86 struct HidbusStatusManager {
87 std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
88 INSERT_PADDING_BYTES(0x680); // Unused
89 };
90 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
91
92 struct HidbusDevice {
93 bool is_device_initializated{};
94 BusHandle handle{};
95 std::unique_ptr<HidbusBase> device{nullptr};
96 };
97
98 void GetBusHandle(Kernel::HLERequestContext& ctx);
99 void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
100 void Initialize(Kernel::HLERequestContext& ctx);
101 void Finalize(Kernel::HLERequestContext& ctx);
102 void EnableExternalDevice(Kernel::HLERequestContext& ctx);
103 void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
104 void SendCommandAsync(Kernel::HLERequestContext& ctx);
105 void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
106 void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
107 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
108 void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
109 void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
110 void SetStatusManagerType(Kernel::HLERequestContext& ctx);
111
112 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
113 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
114
115 template <typename T>
116 void MakeDevice(BusHandle handle) {
117 const auto device_index = GetDeviceIndexFromHandle(handle);
118 if (device_index) {
119 devices[device_index.value()].device =
120 std::make_unique<T>(system.HIDCore(), service_context);
121 }
122 }
123
124 bool is_hidbus_enabled{false};
125 HidbusStatusManager hidbus_status{};
126 std::array<HidbusDevice, max_number_of_handles> devices{};
127 std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
128 KernelHelpers::ServiceContext service_context;
129};
130
131} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..09bff10e5
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,72 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Service::HID {
12
13HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
14 : service_context(service_context_) {
15 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
16}
17HidbusBase::~HidbusBase() = default;
18
19void HidbusBase::ActivateDevice() {
20 if (is_activated) {
21 return;
22 }
23 is_activated = true;
24 OnInit();
25}
26
27void HidbusBase::DeactivateDevice() {
28 if (is_activated) {
29 OnRelease();
30 }
31 is_activated = false;
32}
33
34bool HidbusBase::IsDeviceActivated() const {
35 return is_activated;
36}
37
38void HidbusBase::Enable(bool enable) {
39 device_enabled = enable;
40}
41
42bool HidbusBase::IsEnabled() const {
43 return device_enabled;
44}
45
46bool HidbusBase::IsPollingMode() const {
47 return polling_mode_enabled;
48}
49
50JoyPollingMode HidbusBase::GetPollingMode() const {
51 return polling_mode;
52}
53
54void HidbusBase::SetPollingMode(JoyPollingMode mode) {
55 polling_mode = mode;
56 polling_mode_enabled = true;
57}
58
59void HidbusBase::DisablePollingMode() {
60 polling_mode_enabled = false;
61}
62
63void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
64 is_transfer_memory_set = true;
65 transfer_memory = t_mem;
66}
67
68Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
69 return send_command_async_event->GetReadableEvent();
70}
71
72} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
new file mode 100644
index 000000000..13d073a3d
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,179 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::KernelHelpers {
17class ServiceContext;
18}
19
20namespace Service::HID {
21
22// This is nn::hidbus::JoyPollingMode
23enum class JoyPollingMode : u32 {
24 SixAxisSensorDisable,
25 SixAxisSensorEnable,
26 ButtonOnly,
27};
28
29struct DataAccessorHeader {
30 ResultCode result{ResultUnknown};
31 INSERT_PADDING_WORDS(0x1);
32 std::array<u8, 0x18> unused{};
33 u64 latest_entry{};
34 u64 total_entries{};
35};
36static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
37
38struct JoyDisableSixAxisPollingData {
39 std::array<u8, 0x26> data;
40 u8 out_size;
41 INSERT_PADDING_BYTES(0x1);
42 u64 sampling_number;
43};
44static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
45 "JoyDisableSixAxisPollingData is an invalid size");
46
47struct JoyEnableSixAxisPollingData {
48 std::array<u8, 0x8> data;
49 u8 out_size;
50 INSERT_PADDING_BYTES(0x7);
51 u64 sampling_number;
52};
53static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
54 "JoyEnableSixAxisPollingData is an invalid size");
55
56struct JoyButtonOnlyPollingData {
57 std::array<u8, 0x2c> data;
58 u8 out_size;
59 INSERT_PADDING_BYTES(0x3);
60 u64 sampling_number;
61};
62static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
63 "JoyButtonOnlyPollingData is an invalid size");
64
65struct JoyDisableSixAxisPollingEntry {
66 u64 sampling_number;
67 JoyDisableSixAxisPollingData polling_data;
68};
69static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
70 "JoyDisableSixAxisPollingEntry is an invalid size");
71
72struct JoyEnableSixAxisPollingEntry {
73 u64 sampling_number;
74 JoyEnableSixAxisPollingData polling_data;
75};
76static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
77 "JoyEnableSixAxisPollingEntry is an invalid size");
78
79struct JoyButtonOnlyPollingEntry {
80 u64 sampling_number;
81 JoyButtonOnlyPollingData polling_data;
82};
83static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
84 "JoyButtonOnlyPollingEntry is an invalid size");
85
86struct JoyDisableSixAxisDataAccessor {
87 DataAccessorHeader header{};
88 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
89};
90static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
91 "JoyDisableSixAxisDataAccessor is an invalid size");
92
93struct JoyEnableSixAxisDataAccessor {
94 DataAccessorHeader header{};
95 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
96};
97static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
98 "JoyEnableSixAxisDataAccessor is an invalid size");
99
100struct ButtonOnlyPollingDataAccessor {
101 DataAccessorHeader header;
102 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
103};
104static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
105 "ButtonOnlyPollingDataAccessor is an invalid size");
106
107class HidbusBase {
108public:
109 explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
110 virtual ~HidbusBase();
111
112 void ActivateDevice();
113
114 void DeactivateDevice();
115
116 bool IsDeviceActivated() const;
117
118 // Enables/disables the device
119 void Enable(bool enable);
120
121 // returns true if device is enabled
122 bool IsEnabled() const;
123
124 // returns true if polling mode is enabled
125 bool IsPollingMode() const;
126
127 // returns polling mode
128 JoyPollingMode GetPollingMode() const;
129
130 // Sets and enables JoyPollingMode
131 void SetPollingMode(JoyPollingMode mode);
132
133 // Disables JoyPollingMode
134 void DisablePollingMode();
135
136 // Called on EnableJoyPollingReceiveMode
137 void SetTransferMemoryPointer(u8* t_mem);
138
139 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
140
141 virtual void OnInit() {}
142
143 virtual void OnRelease() {}
144
145 // Updates device transfer memory
146 virtual void OnUpdate() {}
147
148 // Returns the device ID of the joycon
149 virtual u8 GetDeviceId() const {
150 return {};
151 }
152
153 // Assigns a command from data
154 virtual bool SetCommand(const std::vector<u8>& data) {
155 return {};
156 }
157
158 // Returns a reply from a command
159 virtual std::vector<u8> GetReply() const {
160 return {};
161 }
162
163protected:
164 bool is_activated{};
165 bool device_enabled{};
166 bool polling_mode_enabled{};
167 JoyPollingMode polling_mode = {};
168 // TODO(German77): All data accessors need to be replaced with a ring lifo object
169 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
170 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
171 ButtonOnlyPollingDataAccessor button_only_data{};
172
173 u8* transfer_memory{nullptr};
174 bool is_transfer_memory_set{};
175
176 Kernel::KEvent* send_command_async_event;
177 KernelHelpers::ServiceContext& service_context;
178};
179} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
new file mode 100644
index 000000000..5ec3cc83c
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,286 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_devices.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/hid/hidbus/ringcon.h"
10
11namespace Service::HID {
12
13RingController::RingController(Core::HID::HIDCore& hid_core_,
14 KernelHelpers::ServiceContext& service_context_)
15 : HidbusBase(service_context_) {
16 input = hid_core_.GetEmulatedDevices();
17}
18
19RingController::~RingController() = default;
20
21void RingController::OnInit() {
22 return;
23}
24
25void RingController::OnRelease() {
26 return;
27};
28
29void RingController::OnUpdate() {
30 if (!is_activated) {
31 return;
32 }
33
34 if (!device_enabled) {
35 return;
36 }
37
38 if (!polling_mode_enabled || !is_transfer_memory_set) {
39 return;
40 }
41
42 // TODO: Increment multitasking counters from motion and sensor data
43
44 switch (polling_mode) {
45 case JoyPollingMode::SixAxisSensorEnable: {
46 enable_sixaxis_data.header.total_entries = 10;
47 enable_sixaxis_data.header.result = ResultSuccess;
48 const auto& last_entry =
49 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
50
51 enable_sixaxis_data.header.latest_entry =
52 (enable_sixaxis_data.header.latest_entry + 1) % 10;
53 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
54
55 curr_entry.sampling_number = last_entry.sampling_number + 1;
56 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
57
58 const RingConData ringcon_value = GetSensorValue();
59 curr_entry.polling_data.out_size = sizeof(ringcon_value);
60 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
61
62 std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
63 break;
64 }
65 default:
66 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
67 break;
68 }
69}
70
71RingController::RingConData RingController::GetSensorValue() const {
72 RingConData ringcon_sensor_value{
73 .status = DataValid::Valid,
74 .data = 0,
75 };
76
77 const f32 force_value = input->GetRingSensorForce().force * range;
78 ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
79
80 return ringcon_sensor_value;
81}
82
83u8 RingController::GetDeviceId() const {
84 return device_id;
85}
86
87std::vector<u8> RingController::GetReply() const {
88 const RingConCommands current_command = command;
89
90 switch (current_command) {
91 case RingConCommands::GetFirmwareVersion:
92 return GetFirmwareVersionReply();
93 case RingConCommands::ReadId:
94 return GetReadIdReply();
95 case RingConCommands::c20105:
96 return GetC020105Reply();
97 case RingConCommands::ReadUnkCal:
98 return GetReadUnkCalReply();
99 case RingConCommands::ReadFactoryCal:
100 return GetReadFactoryCalReply();
101 case RingConCommands::ReadUserCal:
102 return GetReadUserCalReply();
103 case RingConCommands::ReadRepCount:
104 return GetReadRepCountReply();
105 case RingConCommands::ReadTotalPushCount:
106 return GetReadTotalPushCountReply();
107 case RingConCommands::ResetRepCount:
108 return GetResetRepCountReply();
109 case RingConCommands::SaveCalData:
110 return GetSaveDataReply();
111 default:
112 return GetErrorReply();
113 }
114}
115
116bool RingController::SetCommand(const std::vector<u8>& data) {
117 if (data.size() < 4) {
118 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
119 command = RingConCommands::Error;
120 return false;
121 }
122
123 std::memcpy(&command, data.data(), sizeof(RingConCommands));
124
125 switch (command) {
126 case RingConCommands::GetFirmwareVersion:
127 case RingConCommands::ReadId:
128 case RingConCommands::c20105:
129 case RingConCommands::ReadUnkCal:
130 case RingConCommands::ReadFactoryCal:
131 case RingConCommands::ReadUserCal:
132 case RingConCommands::ReadRepCount:
133 case RingConCommands::ReadTotalPushCount:
134 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
135 send_command_async_event->GetWritableEvent().Signal();
136 return true;
137 case RingConCommands::ResetRepCount:
138 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
139 total_rep_count = 0;
140 send_command_async_event->GetWritableEvent().Signal();
141 return true;
142 case RingConCommands::SaveCalData: {
143 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
144
145 SaveCalData save_info{};
146 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
147 user_calibration = save_info.calibration;
148 send_command_async_event->GetWritableEvent().Signal();
149 return true;
150 }
151 default:
152 LOG_ERROR(Service_HID, "Command not implemented {}", command);
153 command = RingConCommands::Error;
154 // Signal a reply to avoid softlocking the game
155 send_command_async_event->GetWritableEvent().Signal();
156 return false;
157 }
158}
159
160std::vector<u8> RingController::GetFirmwareVersionReply() const {
161 const FirmwareVersionReply reply{
162 .status = DataValid::Valid,
163 .firmware = version,
164 };
165
166 return GetDataVector(reply);
167}
168
169std::vector<u8> RingController::GetReadIdReply() const {
170 // The values are hardcoded from a real joycon
171 const ReadIdReply reply{
172 .status = DataValid::Valid,
173 .id_l_x0 = 8,
174 .id_l_x0_2 = 41,
175 .id_l_x4 = 22294,
176 .id_h_x0 = 19777,
177 .id_h_x0_2 = 13621,
178 .id_h_x4 = 8245,
179 };
180
181 return GetDataVector(reply);
182}
183
184std::vector<u8> RingController::GetC020105Reply() const {
185 const Cmd020105Reply reply{
186 .status = DataValid::Valid,
187 .data = 1,
188 };
189
190 return GetDataVector(reply);
191}
192
193std::vector<u8> RingController::GetReadUnkCalReply() const {
194 const ReadUnkCalReply reply{
195 .status = DataValid::Valid,
196 .data = 0,
197 };
198
199 return GetDataVector(reply);
200}
201
202std::vector<u8> RingController::GetReadFactoryCalReply() const {
203 const ReadFactoryCalReply reply{
204 .status = DataValid::Valid,
205 .calibration = factory_calibration,
206 };
207
208 return GetDataVector(reply);
209}
210
211std::vector<u8> RingController::GetReadUserCalReply() const {
212 const ReadUserCalReply reply{
213 .status = DataValid::Valid,
214 .calibration = user_calibration,
215 };
216
217 return GetDataVector(reply);
218}
219
220std::vector<u8> RingController::GetReadRepCountReply() const {
221 const GetThreeByteReply reply{
222 .status = DataValid::Valid,
223 .data = {total_rep_count, 0, 0},
224 .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
225 };
226
227 return GetDataVector(reply);
228}
229
230std::vector<u8> RingController::GetReadTotalPushCountReply() const {
231 const GetThreeByteReply reply{
232 .status = DataValid::Valid,
233 .data = {total_push_count, 0, 0},
234 .crc = GetCrcValue({total_push_count, 0, 0, 0}),
235 };
236
237 return GetDataVector(reply);
238}
239
240std::vector<u8> RingController::GetResetRepCountReply() const {
241 return GetReadRepCountReply();
242}
243
244std::vector<u8> RingController::GetSaveDataReply() const {
245 const StatusReply reply{
246 .status = DataValid::Valid,
247 };
248
249 return GetDataVector(reply);
250}
251
252std::vector<u8> RingController::GetErrorReply() const {
253 const ErrorReply reply{
254 .status = DataValid::BadCRC,
255 };
256
257 return GetDataVector(reply);
258}
259
260u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
261 u8 crc = 0;
262 for (std::size_t index = 0; index < data.size(); index++) {
263 for (u8 i = 0x80; i > 0; i >>= 1) {
264 bool bit = (crc & 0x80) != 0;
265 if ((data[index] & i) != 0) {
266 bit = !bit;
267 }
268 crc <<= 1;
269 if (bit) {
270 crc ^= 0x8d;
271 }
272 }
273 }
274 return crc;
275}
276
277template <typename T>
278std::vector<u8> RingController::GetDataVector(const T& reply) const {
279 static_assert(std::is_trivially_copyable_v<T>);
280 std::vector<u8> data;
281 data.resize(sizeof(reply));
282 std::memcpy(data.data(), &reply, sizeof(reply));
283 return data;
284}
285
286} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
new file mode 100644
index 000000000..2dbc6150e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,254 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/service/hid/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedDevices;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::HID::HIDCore& hid_core_,
21 KernelHelpers::ServiceContext& service_context_);
22 ~RingController() override;
23
24 void OnInit() override;
25
26 void OnRelease() override;
27
28 // Updates ringcon transfer memory
29 void OnUpdate() override;
30
31 // Returns the device ID of the joycon
32 u8 GetDeviceId() const override;
33
34 // Assigns a command from data
35 bool SetCommand(const std::vector<u8>& data) override;
36
37 // Returns a reply from a command
38 std::vector<u8> GetReply() const override;
39
40private:
41 // These values are obtained from a real ring controller
42 static constexpr s16 idle_value = 2280;
43 static constexpr s16 idle_deadzone = 120;
44 static constexpr s16 range = 2500;
45
46 // Most missing command names are leftovers from other firmware versions
47 enum class RingConCommands : u32 {
48 GetFirmwareVersion = 0x00020000,
49 ReadId = 0x00020100,
50 JoyPolling = 0x00020101,
51 Unknown1 = 0x00020104,
52 c20105 = 0x00020105,
53 Unknown2 = 0x00020204,
54 Unknown3 = 0x00020304,
55 Unknown4 = 0x00020404,
56 ReadUnkCal = 0x00020504,
57 ReadFactoryCal = 0x00020A04,
58 Unknown5 = 0x00021104,
59 Unknown6 = 0x00021204,
60 Unknown7 = 0x00021304,
61 ReadUserCal = 0x00021A04,
62 ReadRepCount = 0x00023104,
63 ReadTotalPushCount = 0x00023204,
64 ResetRepCount = 0x04013104,
65 Unknown8 = 0x04011104,
66 Unknown9 = 0x04011204,
67 Unknown10 = 0x04011304,
68 SaveCalData = 0x10011A04,
69 Error = 0xFFFFFFFF,
70 };
71
72 enum class DataValid : u32 {
73 Valid,
74 BadCRC,
75 Cal,
76 };
77
78 struct FirmwareVersion {
79 u8 sub;
80 u8 main;
81 };
82 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
83
84 struct FactoryCalibration {
85 s32_le os_max;
86 s32_le hk_max;
87 s32_le zero_min;
88 s32_le zero_max;
89 };
90 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
91
92 struct CalibrationValue {
93 s16 value;
94 u16 crc;
95 };
96 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
97
98 struct UserCalibration {
99 CalibrationValue os_max;
100 CalibrationValue hk_max;
101 CalibrationValue zero;
102 };
103 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
104
105 struct SaveCalData {
106 RingConCommands command;
107 UserCalibration calibration;
108 INSERT_PADDING_BYTES_NOINIT(4);
109 };
110 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
111 static_assert(std::is_trivially_copyable_v<SaveCalData>,
112 "SaveCalData must be trivially copyable");
113
114 struct FirmwareVersionReply {
115 DataValid status;
116 FirmwareVersion firmware;
117 INSERT_PADDING_BYTES(0x2);
118 };
119 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
120
121 struct Cmd020105Reply {
122 DataValid status;
123 u8 data;
124 INSERT_PADDING_BYTES(0x3);
125 };
126 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
127
128 struct StatusReply {
129 DataValid status;
130 };
131 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
132
133 struct GetThreeByteReply {
134 DataValid status;
135 std::array<u8, 3> data;
136 u8 crc;
137 };
138 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
139
140 struct ReadUnkCalReply {
141 DataValid status;
142 u16 data;
143 INSERT_PADDING_BYTES(0x2);
144 };
145 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
146
147 struct ReadFactoryCalReply {
148 DataValid status;
149 FactoryCalibration calibration;
150 };
151 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
152
153 struct ReadUserCalReply {
154 DataValid status;
155 UserCalibration calibration;
156 INSERT_PADDING_BYTES(0x4);
157 };
158 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
159
160 struct ReadIdReply {
161 DataValid status;
162 u16 id_l_x0;
163 u16 id_l_x0_2;
164 u16 id_l_x4;
165 u16 id_h_x0;
166 u16 id_h_x0_2;
167 u16 id_h_x4;
168 };
169 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
170
171 struct ErrorReply {
172 DataValid status;
173 INSERT_PADDING_BYTES(0x3);
174 };
175 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
176
177 struct RingConData {
178 DataValid status;
179 s16_le data;
180 INSERT_PADDING_BYTES(0x2);
181 };
182 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
183
184 // Returns RingConData struct with pressure sensor values
185 RingConData GetSensorValue() const;
186
187 // Returns 8 byte reply with firmware version
188 std::vector<u8> GetFirmwareVersionReply() const;
189
190 // Returns 16 byte reply with ID values
191 std::vector<u8> GetReadIdReply() const;
192
193 // (STUBBED) Returns 8 byte reply
194 std::vector<u8> GetC020105Reply() const;
195
196 // (STUBBED) Returns 8 byte empty reply
197 std::vector<u8> GetReadUnkCalReply() const;
198
199 // Returns 20 byte reply with factory calibration values
200 std::vector<u8> GetReadFactoryCalReply() const;
201
202 // Returns 20 byte reply with user calibration values
203 std::vector<u8> GetReadUserCalReply() const;
204
205 // Returns 8 byte reply
206 std::vector<u8> GetReadRepCountReply() const;
207
208 // Returns 8 byte reply
209 std::vector<u8> GetReadTotalPushCountReply() const;
210
211 // Returns 8 byte reply
212 std::vector<u8> GetResetRepCountReply() const;
213
214 // Returns 4 byte save data reply
215 std::vector<u8> GetSaveDataReply() const;
216
217 // Returns 8 byte error reply
218 std::vector<u8> GetErrorReply() const;
219
220 // Returns 8 bit redundancy check from provided data
221 u8 GetCrcValue(const std::vector<u8>& data) const;
222
223 // Converts structs to an u8 vector equivalent
224 template <typename T>
225 std::vector<u8> GetDataVector(const T& reply) const;
226
227 RingConCommands command{RingConCommands::Error};
228
229 // These counters are used in multitasking mode while the switch is sleeping
230 // Total steps taken
231 u8 total_rep_count = 0;
232 // Total times the ring was pushed
233 u8 total_push_count = 0;
234
235 const u8 device_id = 0x20;
236 const FirmwareVersion version = {
237 .sub = 0x0,
238 .main = 0x2c,
239 };
240 const FactoryCalibration factory_calibration = {
241 .os_max = idle_value + range + idle_deadzone,
242 .hk_max = idle_value - range - idle_deadzone,
243 .zero_min = idle_value - idle_deadzone,
244 .zero_max = idle_value + idle_deadzone,
245 };
246 UserCalibration user_calibration = {
247 .os_max = {.value = range, .crc = 228},
248 .hk_max = {.value = -range, .crc = 239},
249 .zero = {.value = idle_value, .crc = 225},
250 };
251
252 Core::HID::EmulatedDevices* input;
253};
254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
new file mode 100644
index 000000000..3175c48da
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/starlink.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0x28;
11
12Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
13 : HidbusBase(service_context_) {}
14Starlink::~Starlink() = default;
15
16void Starlink::OnInit() {
17 return;
18}
19
20void Starlink::OnRelease() {
21 return;
22};
23
24void Starlink::OnUpdate() {
25 if (!is_activated) {
26 return;
27 }
28 if (!device_enabled) {
29 return;
30 }
31 if (!polling_mode_enabled || !is_transfer_memory_set) {
32 return;
33 }
34
35 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
36}
37
38u8 Starlink::GetDeviceId() const {
39 return DEVICE_ID;
40}
41
42std::vector<u8> Starlink::GetReply() const {
43 return {};
44}
45
46bool Starlink::SetCommand(const std::vector<u8>& data) {
47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false;
49}
50
51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
new file mode 100644
index 000000000..79770b68e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class Starlink final : public HidbusBase {
17public:
18 explicit Starlink(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~Starlink() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
new file mode 100644
index 000000000..5474657be
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/stubbed.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0xFF;
11
12HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) {}
15HidbusStubbed::~HidbusStubbed() = default;
16
17void HidbusStubbed::OnInit() {
18 return;
19}
20
21void HidbusStubbed::OnRelease() {
22 return;
23};
24
25void HidbusStubbed::OnUpdate() {
26 if (!is_activated) {
27 return;
28 }
29 if (!device_enabled) {
30 return;
31 }
32 if (!polling_mode_enabled || !is_transfer_memory_set) {
33 return;
34 }
35
36 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
37}
38
39u8 HidbusStubbed::GetDeviceId() const {
40 return DEVICE_ID;
41}
42
43std::vector<u8> HidbusStubbed::GetReply() const {
44 return {};
45}
46
47bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
48 LOG_ERROR(Service_HID, "Command not implemented");
49 return false;
50}
51
52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
new file mode 100644
index 000000000..40acdfe8f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class HidbusStubbed final : public HidbusBase {
17public:
18 explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~HidbusStubbed() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
index c8ebd2e3f..0f9e33ef6 100644
--- a/src/core/hle/service/jit/jit.cpp
+++ b/src/core/hle/service/jit/jit.cpp
@@ -2,27 +2,256 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/arm/symbols.h"
6#include "core/core.h"
5#include "core/hle/ipc_helpers.h" 7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_transfer_memory.h"
6#include "core/hle/result.h" 10#include "core/hle/result.h"
7#include "core/hle/service/jit/jit.h" 11#include "core/hle/service/jit/jit.h"
12#include "core/hle/service/jit/jit_context.h"
8#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14#include "core/memory.h"
9 15
10namespace Service::JIT { 16namespace Service::JIT {
11 17
18struct CodeRange {
19 u64 offset;
20 u64 size;
21};
22
12class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { 23class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
13public: 24public:
14 explicit IJitEnvironment(Core::System& system_) : ServiceFramework{system_, "IJitEnvironment"} { 25 explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
26 : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
27 context{system_.Memory()} {
15 // clang-format off 28 // clang-format off
16 static const FunctionInfo functions[] = { 29 static const FunctionInfo functions[] = {
17 {0, nullptr, "GenerateCode"}, 30 {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
18 {1, nullptr, "Control"}, 31 {1, &IJitEnvironment::Control, "Control"},
19 {1000, nullptr, "LoadPlugin"}, 32 {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
20 {1001, nullptr, "GetCodeAddress"}, 33 {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
21 }; 34 };
22 // clang-format on 35 // clang-format on
23 36
24 RegisterHandlers(functions); 37 RegisterHandlers(functions);
38
39 // Identity map user code range into sysmodule context
40 configuration.user_ro_memory = user_ro;
41 configuration.user_rx_memory = user_rx;
42 configuration.sys_ro_memory = user_ro;
43 configuration.sys_rx_memory = user_rx;
25 } 44 }
45
46 void GenerateCode(Kernel::HLERequestContext& ctx) {
47 struct Parameters {
48 u32 data_size;
49 u64 command;
50 CodeRange cr1;
51 CodeRange cr2;
52 Struct32 data;
53 };
54
55 IPC::RequestParser rp{ctx};
56 const auto parameters{rp.PopRaw<Parameters>()};
57 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
58 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
59
60 const VAddr return_ptr{context.AddHeap(0u)};
61 const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
62 const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
63 const VAddr cr1_out_ptr{
64 context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
65 const VAddr cr2_out_ptr{
66 context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
67 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
68 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
69 const VAddr data_ptr{context.AddHeap(parameters.data)};
70 const VAddr configuration_ptr{context.AddHeap(configuration)};
71
72 context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
73 configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
74 cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
75 output_buffer.size());
76
77 const s32 return_value{context.GetHeap<s32>(return_ptr)};
78
79 if (return_value == 0) {
80 system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
81 configuration.user_rx_memory.size);
82
83 if (ctx.CanWriteBuffer()) {
84 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
85 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
86 }
87 const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
88 const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
89
90 IPC::ResponseBuilder rb{ctx, 8};
91 rb.Push(ResultSuccess);
92 rb.Push<u64>(return_value);
93 rb.PushRaw(cr1_out);
94 rb.PushRaw(cr2_out);
95 } else {
96 LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ResultUnknown);
99 }
100 };
101
102 void Control(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto command{rp.PopRaw<u64>()};
105 const auto input_buffer{ctx.ReadBuffer()};
106 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
107
108 const VAddr return_ptr{context.AddHeap(0u)};
109 const VAddr configuration_ptr{context.AddHeap(configuration)};
110 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
111 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
112 const u64 wrapper_value{
113 context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
114 input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
115 const s32 return_value{context.GetHeap<s32>(return_ptr)};
116
117 if (wrapper_value == 0 && return_value == 0) {
118 if (ctx.CanWriteBuffer()) {
119 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
120 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
121 }
122 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(ResultSuccess);
124 rb.Push(return_value);
125 } else {
126 LOG_WARNING(Service_JIT, "plugin Control callback failed");
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ResultUnknown);
129 }
130 }
131
132 void LoadPlugin(Kernel::HLERequestContext& ctx) {
133 IPC::RequestParser rp{ctx};
134 const auto tmem_size{rp.PopRaw<u64>()};
135 if (tmem_size == 0) {
136 LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
137 IPC::ResponseBuilder rb{ctx, 2};
138 rb.Push(ResultUnknown);
139 return;
140 }
141
142 const auto tmem_handle{ctx.GetCopyHandle(0)};
143 auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
144 tmem_handle)};
145 if (tmem.IsNull()) {
146 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ResultUnknown);
149 return;
150 }
151
152 configuration.work_memory.offset = tmem->GetSourceAddress();
153 configuration.work_memory.size = tmem_size;
154
155 const auto nro_plugin{ctx.ReadBuffer(1)};
156 auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
157 const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
158
159 callbacks =
160 GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
161 .rtld_init = GetSymbol("_init"),
162 .Control = GetSymbol("nnjitpluginControl"),
163 .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
164 .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
165 .Configure = GetSymbol("nnjitpluginConfigure"),
166 .GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
167 .GetVersion = GetSymbol("nnjitpluginGetVersion"),
168 .Keeper = GetSymbol("nnjitpluginKeeper"),
169 .OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
170
171 if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
172 callbacks.OnPrepared == 0) {
173 LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
174 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(ResultUnknown);
176 return;
177 }
178
179 if (!context.LoadNRO(nro_plugin)) {
180 LOG_ERROR(Service_JIT, "failed to load plugin");
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(ResultUnknown);
183 return;
184 }
185
186 context.MapProcessMemory(configuration.sys_ro_memory.offset,
187 configuration.sys_ro_memory.size);
188 context.MapProcessMemory(configuration.sys_rx_memory.offset,
189 configuration.sys_rx_memory.size);
190 context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
191
192 if (callbacks.rtld_init != 0) {
193 context.CallFunction(callbacks.rtld_init);
194 }
195
196 const auto version{context.CallFunction(callbacks.GetVersion)};
197 if (version != 1) {
198 LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultUnknown);
201 return;
202 }
203
204 const auto resolve{context.GetHelper("_resolve")};
205 if (callbacks.ResolveBasicSymbols != 0) {
206 context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
207 }
208 const auto resolve_ptr{context.AddHeap(resolve)};
209 if (callbacks.SetupDiagnostics != 0) {
210 context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
211 }
212
213 context.CallFunction(callbacks.Configure, 0u);
214 const auto configuration_ptr{context.AddHeap(configuration)};
215 context.CallFunction(callbacks.OnPrepared, configuration_ptr);
216
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219 }
220
221 void GetCodeAddress(Kernel::HLERequestContext& ctx) {
222 IPC::ResponseBuilder rb{ctx, 6};
223 rb.Push(ResultSuccess);
224 rb.Push(configuration.user_rx_memory.offset);
225 rb.Push(configuration.user_ro_memory.offset);
226 }
227
228private:
229 using Struct32 = std::array<u8, 32>;
230
231 struct GuestCallbacks {
232 VAddr rtld_fini;
233 VAddr rtld_init;
234 VAddr Control;
235 VAddr ResolveBasicSymbols;
236 VAddr SetupDiagnostics;
237 VAddr Configure;
238 VAddr GenerateCode;
239 VAddr GetVersion;
240 VAddr Keeper;
241 VAddr OnPrepared;
242 };
243
244 struct JITConfiguration {
245 CodeRange user_rx_memory;
246 CodeRange user_ro_memory;
247 CodeRange work_memory;
248 CodeRange sys_rx_memory;
249 CodeRange sys_ro_memory;
250 };
251
252 GuestCallbacks callbacks;
253 JITConfiguration configuration;
254 JITContext context;
26}; 255};
27 256
28class JITU final : public ServiceFramework<JITU> { 257class JITU final : public ServiceFramework<JITU> {
@@ -40,9 +269,59 @@ public:
40 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) { 269 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
41 LOG_DEBUG(Service_JIT, "called"); 270 LOG_DEBUG(Service_JIT, "called");
42 271
272 struct Parameters {
273 u64 rx_size;
274 u64 ro_size;
275 };
276
277 IPC::RequestParser rp{ctx};
278 const auto parameters{rp.PopRaw<Parameters>()};
279 const auto executable_mem_handle{ctx.GetCopyHandle(1)};
280 const auto readable_mem_handle{ctx.GetCopyHandle(2)};
281
282 if (parameters.rx_size == 0 || parameters.ro_size == 0) {
283 LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ResultUnknown);
286 return;
287 }
288
289 // The copy handle at index 0 is the process handle, but handle tables are
290 // per-process, so there is no point reading it here until we are multiprocess
291 const auto& process{*system.CurrentProcess()};
292
293 auto executable_mem{
294 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
295 if (executable_mem.IsNull()) {
296 LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
297 executable_mem_handle);
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(ResultUnknown);
300 return;
301 }
302
303 auto readable_mem{
304 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
305 if (readable_mem.IsNull()) {
306 LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultUnknown);
309 return;
310 }
311
312 const CodeRange user_rx{
313 .offset = executable_mem->GetSourceAddress(),
314 .size = parameters.rx_size,
315 };
316
317 const CodeRange user_ro{
318 .offset = readable_mem->GetSourceAddress(),
319 .size = parameters.ro_size,
320 };
321
43 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 322 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
44 rb.Push(ResultSuccess); 323 rb.Push(ResultSuccess);
45 rb.PushIpcInterface<IJitEnvironment>(system); 324 rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
46 } 325 }
47}; 326};
48 327
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
new file mode 100644
index 000000000..630368fb3
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <map>
7#include <span>
8#include <boost/icl/interval_set.hpp>
9#include <dynarmic/interface/A64/a64.h>
10#include <dynarmic/interface/A64/config.h>
11
12#include "common/alignment.h"
13#include "common/common_funcs.h"
14#include "common/div_ceil.h"
15#include "common/logging/log.h"
16#include "core/hle/service/jit/jit_context.h"
17#include "core/memory.h"
18
19namespace Service::JIT {
20
21constexpr std::array<u8, 4> STOP_ARM64 = {
22 0x01, 0x00, 0x00, 0xd4, // svc #0
23};
24
25constexpr std::array<u8, 8> RESOLVE_ARM64 = {
26 0x21, 0x00, 0x00, 0xd4, // svc #1
27 0xc0, 0x03, 0x5f, 0xd6, // ret
28};
29
30constexpr std::array<u8, 4> PANIC_ARM64 = {
31 0x41, 0x00, 0x00, 0xd4, // svc #2
32};
33
34constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
35 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
36 0x83, 0x01, 0x00, 0x54, // b.lo #+34
37 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
38 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
39 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
40 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
41 0xfc, 0xff, 0xff, 0x17, // b #-16
42 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
43 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
44 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
45 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
46 0x8b, 0xff, 0xff, 0x54, // b.lt #-16
47 0xc0, 0x03, 0x5f, 0xd6, // ret
48 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
49 0xfc, 0xff, 0xff, 0x17, // b #-16
50};
51
52constexpr std::array<u8, 28> MEMSET_ARM64 = {
53 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
54 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
55 0x4b, 0x00, 0x00, 0x54, // b.lt #+8
56 0xc0, 0x03, 0x5f, 0xd6, // ret
57 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
58 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
59 0xfb, 0xff, 0xff, 0x17, // b #-20
60};
61
62struct HelperFunction {
63 const char* name;
64 const std::span<const u8> data;
65};
66
67constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
68 {"_stop", STOP_ARM64},
69 {"_resolve", RESOLVE_ARM64},
70 {"_panic", PANIC_ARM64},
71 {"memcpy", MEMMOVE_ARM64},
72 {"memmove", MEMMOVE_ARM64},
73 {"memset", MEMSET_ARM64},
74}};
75
76struct Elf64_Dyn {
77 u64 d_tag;
78 u64 d_un;
79};
80
81struct Elf64_Rela {
82 u64 r_offset;
83 u64 r_info;
84 s64 r_addend;
85};
86
87static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
88 return static_cast<u32>(rela->r_info);
89}
90
91constexpr int DT_RELA = 7; /* Address of Rela relocs */
92constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
93constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
94
95constexpr size_t STACK_ALIGN = 16;
96
97class JITContextImpl;
98
99using IntervalSet = boost::icl::interval_set<VAddr>::type;
100using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
101
102class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
103public:
104 explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
105 IntervalSet& mapped_ranges_, JITContextImpl& parent_)
106 : memory{memory_}, local_memory{local_memory_},
107 mapped_ranges{mapped_ranges_}, parent{parent_} {}
108
109 u8 MemoryRead8(u64 vaddr) override {
110 return ReadMemory<u8>(vaddr);
111 }
112 u16 MemoryRead16(u64 vaddr) override {
113 return ReadMemory<u16>(vaddr);
114 }
115 u32 MemoryRead32(u64 vaddr) override {
116 return ReadMemory<u32>(vaddr);
117 }
118 u64 MemoryRead64(u64 vaddr) override {
119 return ReadMemory<u64>(vaddr);
120 }
121 u128 MemoryRead128(u64 vaddr) override {
122 return ReadMemory<u128>(vaddr);
123 }
124 std::string MemoryReadCString(u64 vaddr) {
125 std::string result;
126 u8 next;
127
128 while ((next = MemoryRead8(vaddr++)) != 0) {
129 result += next;
130 }
131
132 return result;
133 }
134
135 void MemoryWrite8(u64 vaddr, u8 value) override {
136 WriteMemory<u8>(vaddr, value);
137 }
138 void MemoryWrite16(u64 vaddr, u16 value) override {
139 WriteMemory<u16>(vaddr, value);
140 }
141 void MemoryWrite32(u64 vaddr, u32 value) override {
142 WriteMemory<u32>(vaddr, value);
143 }
144 void MemoryWrite64(u64 vaddr, u64 value) override {
145 WriteMemory<u64>(vaddr, value);
146 }
147 void MemoryWrite128(u64 vaddr, u128 value) override {
148 WriteMemory<u128>(vaddr, value);
149 }
150
151 bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
152 return WriteMemory<u8>(vaddr, value);
153 }
154 bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
155 return WriteMemory<u16>(vaddr, value);
156 }
157 bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
158 return WriteMemory<u32>(vaddr, value);
159 }
160 bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
161 return WriteMemory<u64>(vaddr, value);
162 }
163 bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
164 return WriteMemory<u128>(vaddr, value);
165 }
166
167 void CallSVC(u32 swi) override;
168 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
169 void InterpreterFallback(u64 pc, size_t num_instructions) override;
170
171 void AddTicks(u64 ticks) override {}
172 u64 GetTicksRemaining() override {
173 return std::numeric_limits<u32>::max();
174 }
175 u64 GetCNTPCT() override {
176 return 0;
177 }
178
179 template <class T>
180 T ReadMemory(u64 vaddr) {
181 T ret{};
182 if (boost::icl::contains(mapped_ranges, vaddr)) {
183 memory.ReadBlock(vaddr, &ret, sizeof(T));
184 } else if (vaddr + sizeof(T) > local_memory.size()) {
185 LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
186 } else {
187 std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
188 }
189 return ret;
190 }
191
192 template <class T>
193 bool WriteMemory(u64 vaddr, const T value) {
194 if (boost::icl::contains(mapped_ranges, vaddr)) {
195 memory.WriteBlock(vaddr, &value, sizeof(T));
196 } else if (vaddr + sizeof(T) > local_memory.size()) {
197 LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
198 } else {
199 std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
200 }
201 return true;
202 }
203
204private:
205 Core::Memory::Memory& memory;
206 std::vector<u8>& local_memory;
207 IntervalSet& mapped_ranges;
208 JITContextImpl& parent;
209};
210
211class JITContextImpl {
212public:
213 explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
214 callbacks =
215 std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
216 user_config.callbacks = callbacks.get();
217 jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
218 }
219
220 bool LoadNRO(std::span<const u8> data) {
221 local_memory.clear();
222 local_memory.insert(local_memory.end(), data.begin(), data.end());
223
224 if (FixupRelocations()) {
225 InsertHelperFunctions();
226 InsertStack();
227 return true;
228 } else {
229 return false;
230 }
231 }
232
233 bool FixupRelocations() {
234 const VAddr mod_offset{callbacks->MemoryRead32(4)};
235 if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
236 return false;
237 }
238
239 VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
240 VAddr rela_dyn = 0;
241 size_t num_rela = 0;
242 while (true) {
243 const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
244 dynamic_offset += sizeof(Elf64_Dyn);
245
246 if (!dyn.d_tag) {
247 break;
248 }
249 if (dyn.d_tag == DT_RELA) {
250 rela_dyn = dyn.d_un;
251 }
252 if (dyn.d_tag == DT_RELASZ) {
253 num_rela = dyn.d_un / sizeof(Elf64_Rela);
254 }
255 }
256
257 for (size_t i = 0; i < num_rela; i++) {
258 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
259 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
260 continue;
261 }
262 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
263 callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
264 }
265
266 return true;
267 }
268
269 void InsertHelperFunctions() {
270 for (const auto& [name, contents] : HELPER_FUNCTIONS) {
271 helpers[name] = local_memory.size();
272 local_memory.insert(local_memory.end(), contents.begin(), contents.end());
273 }
274 }
275
276 void InsertStack() {
277 const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
278 local_memory.size()};
279 local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
280 top_of_stack = local_memory.size();
281 heap_pointer = top_of_stack;
282 }
283
284 void MapProcessMemory(VAddr dest_address, std::size_t size) {
285 mapped_ranges.add(IntervalType{dest_address, dest_address + size});
286 }
287
288 void PushArgument(const void* data, size_t size) {
289 const size_t num_words = Common::DivCeil(size, sizeof(u64));
290 const size_t current_pos = argument_stack.size();
291 argument_stack.insert(argument_stack.end(), num_words, 0);
292 std::memcpy(argument_stack.data() + current_pos, data, size);
293 }
294
295 void SetupArguments() {
296 for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
297 jit->SetRegister(i, argument_stack[i]);
298 }
299 if (argument_stack.size() > 8) {
300 const VAddr new_sp = Common::AlignDown(
301 top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
302 for (size_t i = 8; i < argument_stack.size(); i++) {
303 callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
304 }
305 jit->SetSP(new_sp);
306 }
307 argument_stack.clear();
308 heap_pointer = top_of_stack;
309 }
310
311 u64 CallFunction(VAddr func) {
312 jit->SetRegister(30, helpers["_stop"]);
313 jit->SetSP(top_of_stack);
314 SetupArguments();
315
316 jit->SetPC(func);
317 jit->Run();
318 return jit->GetRegister(0);
319 }
320
321 VAddr GetHelper(const std::string& name) {
322 return helpers[name];
323 }
324
325 VAddr AddHeap(const void* data, size_t size) {
326 const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
327 if (heap_pointer + num_bytes > local_memory.size()) {
328 local_memory.insert(local_memory.end(),
329 (heap_pointer + num_bytes) - local_memory.size(), 0);
330 }
331 const VAddr location{heap_pointer};
332 std::memcpy(local_memory.data() + location, data, size);
333 heap_pointer += num_bytes;
334 return location;
335 }
336
337 void GetHeap(VAddr location, void* data, size_t size) {
338 std::memcpy(data, local_memory.data() + location, size);
339 }
340
341 std::unique_ptr<DynarmicCallbacks64> callbacks;
342 std::vector<u8> local_memory;
343 std::vector<u64> argument_stack;
344 IntervalSet mapped_ranges;
345 Dynarmic::A64::UserConfig user_config;
346 std::unique_ptr<Dynarmic::A64::Jit> jit;
347 std::map<std::string, VAddr, std::less<>> helpers;
348 Core::Memory::Memory& memory;
349 VAddr top_of_stack;
350 VAddr heap_pointer;
351};
352
353void DynarmicCallbacks64::CallSVC(u32 swi) {
354 switch (swi) {
355 case 0:
356 parent.jit->HaltExecution();
357 break;
358
359 case 1: {
360 // X0 contains a char* for a symbol to resolve
361 std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
362 const auto helper{parent.helpers[name]};
363
364 if (helper != 0) {
365 parent.jit->SetRegister(0, helper);
366 } else {
367 LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
368 parent.jit->SetRegister(0, parent.helpers["_panic"]);
369 }
370 break;
371 }
372
373 case 2:
374 default:
375 LOG_CRITICAL(Service_JIT, "plugin panicked!");
376 parent.jit->HaltExecution();
377 break;
378 }
379}
380
381void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
382 LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
383 parent.jit->HaltExecution();
384}
385
386void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
387 LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
388 parent.jit->HaltExecution();
389}
390
391JITContext::JITContext(Core::Memory::Memory& memory)
392 : impl{std::make_unique<JITContextImpl>(memory)} {}
393
394JITContext::~JITContext() {}
395
396bool JITContext::LoadNRO(std::span<const u8> data) {
397 return impl->LoadNRO(data);
398}
399
400void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
401 impl->MapProcessMemory(dest_address, size);
402}
403
404u64 JITContext::CallFunction(VAddr func) {
405 return impl->CallFunction(func);
406}
407
408void JITContext::PushArgument(const void* data, size_t size) {
409 impl->PushArgument(data, size);
410}
411
412VAddr JITContext::GetHelper(const std::string& name) {
413 return impl->GetHelper(name);
414}
415
416VAddr JITContext::AddHeap(const void* data, size_t size) {
417 return impl->AddHeap(data, size);
418}
419
420void JITContext::GetHeap(VAddr location, void* data, size_t size) {
421 impl->GetHeap(location, data, size);
422}
423
424} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h
new file mode 100644
index 000000000..d8bf76cff
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.h
@@ -0,0 +1,65 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace Service::JIT {
18
19class JITContextImpl;
20
21class JITContext {
22public:
23 explicit JITContext(Core::Memory::Memory& memory);
24 ~JITContext();
25
26 [[nodiscard]] bool LoadNRO(std::span<const u8> data);
27 void MapProcessMemory(VAddr dest_address, std::size_t size);
28
29 template <typename T, typename... Ts>
30 u64 CallFunction(VAddr func, T argument, Ts... rest) {
31 static_assert(std::is_trivially_copyable_v<T>);
32 PushArgument(&argument, sizeof(argument));
33
34 if constexpr (sizeof...(rest) > 0) {
35 return CallFunction(func, rest...);
36 } else {
37 return CallFunction(func);
38 }
39 }
40
41 u64 CallFunction(VAddr func);
42 VAddr GetHelper(const std::string& name);
43
44 template <typename T>
45 VAddr AddHeap(T argument) {
46 return AddHeap(&argument, sizeof(argument));
47 }
48 VAddr AddHeap(const void* data, size_t size);
49
50 template <typename T>
51 T GetHeap(VAddr location) {
52 static_assert(std::is_trivially_copyable_v<T>);
53 T result;
54 GetHeap(location, &result, sizeof(result));
55 return result;
56 }
57 void GetHeap(VAddr location, void* data, size_t size);
58
59private:
60 std::unique_ptr<JITContextImpl> impl;
61
62 void PushArgument(const void* data, size_t size);
63};
64
65} // namespace Service::JIT
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index cf727c167..42f9cf811 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -160,7 +160,8 @@ public:
160 160
161class RelocatableObject final : public ServiceFramework<RelocatableObject> { 161class RelocatableObject final : public ServiceFramework<RelocatableObject> {
162public: 162public:
163 explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { 163 explicit RelocatableObject(Core::System& system_)
164 : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
164 // clang-format off 165 // clang-format off
165 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
166 {0, &RelocatableObject::LoadModule, "LoadModule"}, 167 {0, &RelocatableObject::LoadModule, "LoadModule"},
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c78b2baeb..148265218 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -9,7 +9,6 @@
9#include <string> 9#include <string>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
13#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -90,7 +89,7 @@ protected:
90 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); 89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
91 90
92 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
93 [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { 92 [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
94 return std::scoped_lock{lock_service}; 93 return std::scoped_lock{lock_service};
95 } 94 }
96 95
@@ -135,7 +134,7 @@ private:
135 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; 134 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
136 135
137 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 136 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
138 Common::SpinLock lock_service; 137 std::mutex lock_service;
139}; 138};
140 139
141/** 140/**
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 21bfb76a4..3f2bf6294 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1311,7 +1311,20 @@ void BufferCache<P>::UpdateVertexBuffer(u32 index) {
1311 const GPUVAddr gpu_addr_begin = array.StartAddress(); 1311 const GPUVAddr gpu_addr_begin = array.StartAddress();
1312 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1; 1312 const GPUVAddr gpu_addr_end = limit.LimitAddress() + 1;
1313 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin); 1313 const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr_begin);
1314 const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); 1314 u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin);
1315 if (address_size >= 64_MiB) {
1316 // Reported vertex buffer size is very large, cap to mapped buffer size
1317 GPUVAddr submapped_addr_end = gpu_addr_begin;
1318
1319 const auto ranges{gpu_memory.GetSubmappedRange(gpu_addr_begin, address_size)};
1320 if (ranges.size() > 0) {
1321 const auto& [addr, size] = *ranges.begin();
1322 submapped_addr_end = addr + size;
1323 }
1324
1325 address_size =
1326 std::min(address_size, static_cast<u32>(submapped_addr_end - gpu_addr_begin));
1327 }
1315 const u32 size = address_size; // TODO: Analyze stride and number of vertices 1328 const u32 size = address_size; // TODO: Analyze stride and number of vertices
1316 if (array.enable == 0 || size == 0 || !cpu_addr) { 1329 if (array.enable == 0 || size == 0 || !cpu_addr) {
1317 vertex_buffers[index] = NULL_BINDING; 1330 vertex_buffers[index] = NULL_BINDING;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 54a902f56..7399e760f 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -214,6 +214,11 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
214 regs.index_array.first = regs.small_index.first; 214 regs.index_array.first = regs.small_index.first;
215 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 215 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
216 return DrawArrays(); 216 return DrawArrays();
217 case MAXWELL3D_REG_INDEX(small_index_2):
218 regs.index_array.count = regs.small_index_2.count;
219 regs.index_array.first = regs.small_index_2.first;
220 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
221 return DrawArrays();
217 case MAXWELL3D_REG_INDEX(topology_override): 222 case MAXWELL3D_REG_INDEX(topology_override):
218 use_topology_override = true; 223 use_topology_override = true;
219 return; 224 return;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3f5b38e55..d36dc3daa 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1220,7 +1220,12 @@ public:
1220 BitField<16, 16, u32> count; 1220 BitField<16, 16, u32> count;
1221 } small_index; 1221 } small_index;
1222 1222
1223 INSERT_PADDING_WORDS_NOINIT(0x6); 1223 union {
1224 BitField<0, 16, u32> first;
1225 BitField<16, 16, u32> count;
1226 } small_index_2;
1227
1228 INSERT_PADDING_WORDS_NOINIT(0x5);
1224 1229
1225 INSERT_PADDING_WORDS_NOINIT(0x1F); 1230 INSERT_PADDING_WORDS_NOINIT(0x1F);
1226 1231
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index d12076358..f8c6e5c7e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -182,6 +182,26 @@ GLenum AttachmentType(PixelFormat format) {
182 } 182 }
183} 183}
184 184
185GLint ConvertA5B5G5R1_UNORM(SwizzleSource source) {
186 switch (source) {
187 case SwizzleSource::Zero:
188 return GL_ZERO;
189 case SwizzleSource::R:
190 return GL_ALPHA;
191 case SwizzleSource::G:
192 return GL_BLUE;
193 case SwizzleSource::B:
194 return GL_GREEN;
195 case SwizzleSource::A:
196 return GL_RED;
197 case SwizzleSource::OneInt:
198 case SwizzleSource::OneFloat:
199 return GL_ONE;
200 }
201 UNREACHABLE_MSG("Invalid swizzle source={}", source);
202 return GL_NONE;
203}
204
185void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) { 205void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4> swizzle) {
186 switch (format) { 206 switch (format) {
187 case PixelFormat::D24_UNORM_S8_UINT: 207 case PixelFormat::D24_UNORM_S8_UINT:
@@ -192,6 +212,12 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
192 TextureMode(format, swizzle[0] == SwizzleSource::R)); 212 TextureMode(format, swizzle[0] == SwizzleSource::R));
193 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 213 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
194 break; 214 break;
215 case PixelFormat::A5B5G5R1_UNORM: {
216 std::array<GLint, 4> gl_swizzle;
217 std::ranges::transform(swizzle, gl_swizzle.begin(), ConvertA5B5G5R1_UNORM);
218 glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
219 return;
220 }
195 default: 221 default:
196 break; 222 break;
197 } 223 }
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index db5bf1d30..03adf3d4c 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -30,6 +30,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM 30 {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UNORM
31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT 31 {GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10_UINT
32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM 32 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5_UNORM
33 {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // A5B5G5R1_UNORM
33 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM 34 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8_UNORM
34 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM 35 {GL_R8_SNORM, GL_RED, GL_BYTE}, // R8_SNORM
35 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT 36 {GL_R8I, GL_RED_INTEGER, GL_BYTE}, // R8_SINT
@@ -87,6 +88,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
87 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB 88 {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // BC3_SRGB
88 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB 89 {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7_SRGB
89 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM 90 {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // A4B4G4R4_UNORM
91 {GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R4G4_UNORM
90 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB 92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
91 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB 93 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
92 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB 94 {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 1c136c410..a2c6d0e6c 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -127,6 +127,7 @@ struct FormatTuple {
127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM 127 {VK_FORMAT_A2B10G10R10_UNORM_PACK32, Attachable | Storage}, // A2B10G10R10_UNORM
128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT 128 {VK_FORMAT_A2B10G10R10_UINT_PACK32, Attachable | Storage}, // A2B10G10R10_UINT
129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle) 129 {VK_FORMAT_A1R5G5B5_UNORM_PACK16, Attachable}, // A1B5G5R5_UNORM (flipped with swizzle)
130 {VK_FORMAT_R5G5B5A1_UNORM_PACK16}, // A5B5G5R1_UNORM (specially swizzled)
130 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM 131 {VK_FORMAT_R8_UNORM, Attachable | Storage}, // R8_UNORM
131 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM 132 {VK_FORMAT_R8_SNORM, Attachable | Storage}, // R8_SNORM
132 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT 133 {VK_FORMAT_R8_SINT, Attachable | Storage}, // R8_SINT
@@ -184,6 +185,7 @@ struct FormatTuple {
184 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB 185 {VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
185 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB 186 {VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
186 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM 187 {VK_FORMAT_R4G4B4A4_UNORM_PACK16, Attachable}, // A4B4G4R4_UNORM
188 {VK_FORMAT_R4G4_UNORM_PACK8}, // R4G4_UNORM
187 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB 189 {VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
188 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB 190 {VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
189 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB 191 {VK_FORMAT_ASTC_8x5_SRGB_BLOCK}, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 2c2ccc7c6..49691ce0c 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -438,6 +438,32 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
438 } 438 }
439} 439}
440 440
441[[nodiscard]] SwizzleSource SwapGreenRed(SwizzleSource value) {
442 switch (value) {
443 case SwizzleSource::R:
444 return SwizzleSource::G;
445 case SwizzleSource::G:
446 return SwizzleSource::R;
447 default:
448 return value;
449 }
450}
451
452[[nodiscard]] SwizzleSource SwapSpecial(SwizzleSource value) {
453 switch (value) {
454 case SwizzleSource::A:
455 return SwizzleSource::R;
456 case SwizzleSource::R:
457 return SwizzleSource::A;
458 case SwizzleSource::G:
459 return SwizzleSource::B;
460 case SwizzleSource::B:
461 return SwizzleSource::G;
462 default:
463 return value;
464 }
465}
466
441void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, 467void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image,
442 VkImageAspectFlags aspect_mask, bool is_initialized, 468 VkImageAspectFlags aspect_mask, bool is_initialized,
443 std::span<const VkBufferImageCopy> copies) { 469 std::span<const VkBufferImageCopy> copies) {
@@ -554,14 +580,25 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
554 }; 580 };
555} 581}
556 582
557[[nodiscard]] bool IsFormatFlipped(PixelFormat format, bool emulate_bgr565) { 583void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
584 bool emulate_bgr565) {
558 switch (format) { 585 switch (format) {
559 case PixelFormat::A1B5G5R5_UNORM: 586 case PixelFormat::A1B5G5R5_UNORM:
560 return true; 587 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
588 break;
561 case PixelFormat::B5G6R5_UNORM: 589 case PixelFormat::B5G6R5_UNORM:
562 return emulate_bgr565; 590 if (emulate_bgr565) {
591 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
592 }
593 break;
594 case PixelFormat::A5B5G5R1_UNORM:
595 std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial);
596 break;
597 case PixelFormat::R4G4_UNORM:
598 std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
599 break;
563 default: 600 default:
564 return false; 601 break;
565 } 602 }
566} 603}
567 604
@@ -1496,9 +1533,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
1496 }; 1533 };
1497 if (!info.IsRenderTarget()) { 1534 if (!info.IsRenderTarget()) {
1498 swizzle = info.Swizzle(); 1535 swizzle = info.Swizzle();
1499 if (IsFormatFlipped(format, device->MustEmulateBGR565())) { 1536 TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
1500 std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
1501 }
1502 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { 1537 if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
1503 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); 1538 std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
1504 } 1539 }
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 5704cf16a..86fea61ae 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -25,6 +25,7 @@ enum class PixelFormat {
25 A2B10G10R10_UNORM, 25 A2B10G10R10_UNORM,
26 A2B10G10R10_UINT, 26 A2B10G10R10_UINT,
27 A1B5G5R5_UNORM, 27 A1B5G5R5_UNORM,
28 A5B5G5R1_UNORM,
28 R8_UNORM, 29 R8_UNORM,
29 R8_SNORM, 30 R8_SNORM,
30 R8_SINT, 31 R8_SINT,
@@ -82,6 +83,7 @@ enum class PixelFormat {
82 BC3_SRGB, 83 BC3_SRGB,
83 BC7_SRGB, 84 BC7_SRGB,
84 A4B4G4R4_UNORM, 85 A4B4G4R4_UNORM,
86 R4G4_UNORM,
85 ASTC_2D_4X4_SRGB, 87 ASTC_2D_4X4_SRGB,
86 ASTC_2D_8X8_SRGB, 88 ASTC_2D_8X8_SRGB,
87 ASTC_2D_8X5_SRGB, 89 ASTC_2D_8X5_SRGB,
@@ -156,6 +158,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
156 1, // A2B10G10R10_UNORM 158 1, // A2B10G10R10_UNORM
157 1, // A2B10G10R10_UINT 159 1, // A2B10G10R10_UINT
158 1, // A1B5G5R5_UNORM 160 1, // A1B5G5R5_UNORM
161 1, // A5B5G5R1_UNORM
159 1, // R8_UNORM 162 1, // R8_UNORM
160 1, // R8_SNORM 163 1, // R8_SNORM
161 1, // R8_SINT 164 1, // R8_SINT
@@ -213,6 +216,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
213 4, // BC3_SRGB 216 4, // BC3_SRGB
214 4, // BC7_SRGB 217 4, // BC7_SRGB
215 1, // A4B4G4R4_UNORM 218 1, // A4B4G4R4_UNORM
219 1, // R4G4_UNORM
216 4, // ASTC_2D_4X4_SRGB 220 4, // ASTC_2D_4X4_SRGB
217 8, // ASTC_2D_8X8_SRGB 221 8, // ASTC_2D_8X8_SRGB
218 8, // ASTC_2D_8X5_SRGB 222 8, // ASTC_2D_8X5_SRGB
@@ -256,6 +260,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
256 1, // A2B10G10R10_UNORM 260 1, // A2B10G10R10_UNORM
257 1, // A2B10G10R10_UINT 261 1, // A2B10G10R10_UINT
258 1, // A1B5G5R5_UNORM 262 1, // A1B5G5R5_UNORM
263 1, // A5B5G5R1_UNORM
259 1, // R8_UNORM 264 1, // R8_UNORM
260 1, // R8_SNORM 265 1, // R8_SNORM
261 1, // R8_SINT 266 1, // R8_SINT
@@ -313,6 +318,7 @@ constexpr std::array<u32, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
313 4, // BC3_SRGB 318 4, // BC3_SRGB
314 4, // BC7_SRGB 319 4, // BC7_SRGB
315 1, // A4B4G4R4_UNORM 320 1, // A4B4G4R4_UNORM
321 1, // R4G4_UNORM
316 4, // ASTC_2D_4X4_SRGB 322 4, // ASTC_2D_4X4_SRGB
317 8, // ASTC_2D_8X8_SRGB 323 8, // ASTC_2D_8X8_SRGB
318 5, // ASTC_2D_8X5_SRGB 324 5, // ASTC_2D_8X5_SRGB
@@ -356,6 +362,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
356 32, // A2B10G10R10_UNORM 362 32, // A2B10G10R10_UNORM
357 32, // A2B10G10R10_UINT 363 32, // A2B10G10R10_UINT
358 16, // A1B5G5R5_UNORM 364 16, // A1B5G5R5_UNORM
365 16, // A5B5G5R1_UNORM
359 8, // R8_UNORM 366 8, // R8_UNORM
360 8, // R8_SNORM 367 8, // R8_SNORM
361 8, // R8_SINT 368 8, // R8_SINT
@@ -413,6 +420,7 @@ constexpr std::array<u32, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
413 128, // BC3_SRGB 420 128, // BC3_SRGB
414 128, // BC7_UNORM 421 128, // BC7_UNORM
415 16, // A4B4G4R4_UNORM 422 16, // A4B4G4R4_UNORM
423 8, // R4G4_UNORM
416 128, // ASTC_2D_4X4_SRGB 424 128, // ASTC_2D_4X4_SRGB
417 128, // ASTC_2D_8X8_SRGB 425 128, // ASTC_2D_8X8_SRGB
418 128, // ASTC_2D_8X5_SRGB 426 128, // ASTC_2D_8X5_SRGB
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index afa807d5d..20e64a7c2 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -63,6 +63,10 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
63 return PixelFormat::A1B5G5R5_UNORM; 63 return PixelFormat::A1B5G5R5_UNORM;
64 case Hash(TextureFormat::A4B4G4R4, UNORM): 64 case Hash(TextureFormat::A4B4G4R4, UNORM):
65 return PixelFormat::A4B4G4R4_UNORM; 65 return PixelFormat::A4B4G4R4_UNORM;
66 case Hash(TextureFormat::G4R4, UNORM):
67 return PixelFormat::R4G4_UNORM;
68 case Hash(TextureFormat::A5B5G5R1, UNORM):
69 return PixelFormat::A5B5G5R1_UNORM;
66 case Hash(TextureFormat::R8, UNORM): 70 case Hash(TextureFormat::R8, UNORM):
67 return PixelFormat::R8_UNORM; 71 return PixelFormat::R8_UNORM;
68 case Hash(TextureFormat::R8, SNORM): 72 case Hash(TextureFormat::R8, SNORM):
@@ -143,6 +147,8 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
143 return PixelFormat::S8_UINT_D24_UNORM; 147 return PixelFormat::S8_UINT_D24_UNORM;
144 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR): 148 case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
145 return PixelFormat::S8_UINT_D24_UNORM; 149 return PixelFormat::S8_UINT_D24_UNORM;
150 case Hash(TextureFormat::D24S8, UNORM, UINT, UINT, UINT, LINEAR):
151 return PixelFormat::D24_UNORM_S8_UINT;
146 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR): 152 case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
147 return PixelFormat::D32_FLOAT_S8_UINT; 153 return PixelFormat::D32_FLOAT_S8_UINT;
148 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR): 154 case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR):
diff --git a/src/video_core/texture_cache/formatter.h b/src/video_core/texture_cache/formatter.h
index b2c81057b..6f5afc5a9 100644
--- a/src/video_core/texture_cache/formatter.h
+++ b/src/video_core/texture_cache/formatter.h
@@ -38,6 +38,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
38 return "A2B10G10R10_UINT"; 38 return "A2B10G10R10_UINT";
39 case PixelFormat::A1B5G5R5_UNORM: 39 case PixelFormat::A1B5G5R5_UNORM:
40 return "A1B5G5R5_UNORM"; 40 return "A1B5G5R5_UNORM";
41 case PixelFormat::A5B5G5R1_UNORM:
42 return "A5B5G5R1_UNORM";
41 case PixelFormat::R8_UNORM: 43 case PixelFormat::R8_UNORM:
42 return "R8_UNORM"; 44 return "R8_UNORM";
43 case PixelFormat::R8_SNORM: 45 case PixelFormat::R8_SNORM:
@@ -152,6 +154,8 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
152 return "BC7_SRGB"; 154 return "BC7_SRGB";
153 case PixelFormat::A4B4G4R4_UNORM: 155 case PixelFormat::A4B4G4R4_UNORM:
154 return "A4B4G4R4_UNORM"; 156 return "A4B4G4R4_UNORM";
157 case PixelFormat::R4G4_UNORM:
158 return "R4G4_UNORM";
155 case PixelFormat::ASTC_2D_4X4_SRGB: 159 case PixelFormat::ASTC_2D_4X4_SRGB:
156 return "ASTC_2D_4X4_SRGB"; 160 return "ASTC_2D_4X4_SRGB";
157 case PixelFormat::ASTC_2D_8X8_SRGB: 161 case PixelFormat::ASTC_2D_8X8_SRGB:
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index f3a05ada9..bd05a1f84 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -45,6 +45,12 @@ constexpr std::array B5G6R5_UNORM_PACK16{
45 VK_FORMAT_R5G6B5_UNORM_PACK16, 45 VK_FORMAT_R5G6B5_UNORM_PACK16,
46 VK_FORMAT_UNDEFINED, 46 VK_FORMAT_UNDEFINED,
47}; 47};
48
49constexpr std::array R4G4_UNORM_PACK8{
50 VK_FORMAT_R8_UNORM,
51 VK_FORMAT_UNDEFINED,
52};
53
48} // namespace Alternatives 54} // namespace Alternatives
49 55
50enum class NvidiaArchitecture { 56enum class NvidiaArchitecture {
@@ -95,6 +101,8 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
95 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data(); 101 return Alternatives::DEPTH16_UNORM_STENCIL8_UINT.data();
96 case VK_FORMAT_B5G6R5_UNORM_PACK16: 102 case VK_FORMAT_B5G6R5_UNORM_PACK16:
97 return Alternatives::B5G6R5_UNORM_PACK16.data(); 103 return Alternatives::B5G6R5_UNORM_PACK16.data();
104 case VK_FORMAT_R4G4_UNORM_PACK8:
105 return Alternatives::R4G4_UNORM_PACK8.data();
98 default: 106 default:
99 return nullptr; 107 return nullptr;
100 } 108 }
@@ -122,6 +130,8 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
122 VK_FORMAT_A8B8G8R8_SRGB_PACK32, 130 VK_FORMAT_A8B8G8R8_SRGB_PACK32,
123 VK_FORMAT_R5G6B5_UNORM_PACK16, 131 VK_FORMAT_R5G6B5_UNORM_PACK16,
124 VK_FORMAT_B5G6R5_UNORM_PACK16, 132 VK_FORMAT_B5G6R5_UNORM_PACK16,
133 VK_FORMAT_R5G5B5A1_UNORM_PACK16,
134 VK_FORMAT_B5G5R5A1_UNORM_PACK16,
125 VK_FORMAT_A2B10G10R10_UNORM_PACK32, 135 VK_FORMAT_A2B10G10R10_UNORM_PACK32,
126 VK_FORMAT_A2B10G10R10_UINT_PACK32, 136 VK_FORMAT_A2B10G10R10_UINT_PACK32,
127 VK_FORMAT_A1R5G5B5_UNORM_PACK16, 137 VK_FORMAT_A1R5G5B5_UNORM_PACK16,
@@ -160,7 +170,9 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
160 VK_FORMAT_R16G16B16A16_SFLOAT, 170 VK_FORMAT_R16G16B16A16_SFLOAT,
161 VK_FORMAT_B8G8R8A8_UNORM, 171 VK_FORMAT_B8G8R8A8_UNORM,
162 VK_FORMAT_B8G8R8A8_SRGB, 172 VK_FORMAT_B8G8R8A8_SRGB,
173 VK_FORMAT_R4G4_UNORM_PACK8,
163 VK_FORMAT_R4G4B4A4_UNORM_PACK16, 174 VK_FORMAT_R4G4B4A4_UNORM_PACK16,
175 VK_FORMAT_B4G4R4A4_UNORM_PACK16,
164 VK_FORMAT_D32_SFLOAT, 176 VK_FORMAT_D32_SFLOAT,
165 VK_FORMAT_D16_UNORM, 177 VK_FORMAT_D16_UNORM,
166 VK_FORMAT_S8_UINT, 178 VK_FORMAT_S8_UINT,
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index b1467d016..2ee21f751 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -99,6 +99,9 @@ add_executable(yuzu
99 configuration/configure_profile_manager.cpp 99 configuration/configure_profile_manager.cpp
100 configuration/configure_profile_manager.h 100 configuration/configure_profile_manager.h
101 configuration/configure_profile_manager.ui 101 configuration/configure_profile_manager.ui
102 configuration/configure_ringcon.cpp
103 configuration/configure_ringcon.h
104 configuration/configure_ringcon.ui
102 configuration/configure_network.cpp 105 configuration/configure_network.cpp
103 configuration/configure_network.h 106 configuration/configure_network.h
104 configuration/configure_network.ui 107 configuration/configure_network.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index d2e735f48..ac26b885b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -60,6 +60,11 @@ const std::array<int, 2> Config::default_stick_mod = {
60 0, 60 0,
61}; 61};
62 62
63const std::array<int, 2> Config::default_ringcon_analogs{{
64 Qt::Key_A,
65 Qt::Key_D,
66}};
67
63// This shouldn't have anything except static initializers (no functions). So 68// This shouldn't have anything except static initializers (no functions). So
64// QKeySequence(...).toString() is NOT ALLOWED HERE. 69// QKeySequence(...).toString() is NOT ALLOWED HERE.
65// This must be in alphabetical order according to action name as it must have the same order as 70// This must be in alphabetical order according to action name as it must have the same order as
@@ -346,6 +351,23 @@ void Config::ReadTouchscreenValues() {
346 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); 351 ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
347} 352}
348 353
354void Config::ReadHidbusValues() {
355 Settings::values.enable_ring_controller =
356 ReadSetting(QStringLiteral("enable_ring_controller"), true).toBool();
357
358 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
359 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
360 auto& ringcon_analogs = Settings::values.ringcon_analogs;
361
362 ringcon_analogs =
363 qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param))
364 .toString()
365 .toStdString();
366 if (ringcon_analogs.empty()) {
367 ringcon_analogs = default_param;
368 }
369}
370
349void Config::ReadAudioValues() { 371void Config::ReadAudioValues() {
350 qt_config->beginGroup(QStringLiteral("Audio")); 372 qt_config->beginGroup(QStringLiteral("Audio"));
351 373
@@ -369,6 +391,7 @@ void Config::ReadControlValues() {
369 ReadMouseValues(); 391 ReadMouseValues();
370 ReadTouchscreenValues(); 392 ReadTouchscreenValues();
371 ReadMotionTouchValues(); 393 ReadMotionTouchValues();
394 ReadHidbusValues();
372 395
373#ifdef _WIN32 396#ifdef _WIN32
374 ReadBasicSetting(Settings::values.enable_raw_input); 397 ReadBasicSetting(Settings::values.enable_raw_input);
@@ -962,6 +985,16 @@ void Config::SaveMotionTouchValues() {
962 qt_config->endArray(); 985 qt_config->endArray();
963} 986}
964 987
988void Config::SaveHidbusValues() {
989 WriteBasicSetting(Settings::values.enable_ring_controller);
990
991 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
992 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f);
993 WriteSetting(QStringLiteral("ring_controller"),
994 QString::fromStdString(Settings::values.ringcon_analogs),
995 QString::fromStdString(default_param));
996}
997
965void Config::SaveValues() { 998void Config::SaveValues() {
966 if (global) { 999 if (global) {
967 SaveControlValues(); 1000 SaveControlValues();
@@ -1002,6 +1035,7 @@ void Config::SaveControlValues() {
1002 SaveMouseValues(); 1035 SaveMouseValues();
1003 SaveTouchscreenValues(); 1036 SaveTouchscreenValues();
1004 SaveMotionTouchValues(); 1037 SaveMotionTouchValues();
1038 SaveHidbusValues();
1005 1039
1006 WriteGlobalSetting(Settings::values.use_docked_mode); 1040 WriteGlobalSetting(Settings::values.use_docked_mode);
1007 WriteGlobalSetting(Settings::values.vibration_enabled); 1041 WriteGlobalSetting(Settings::values.vibration_enabled);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ae3e36a11..f0ab6bdaa 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,7 @@ public:
42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions; 42 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 43 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
44 static const std::array<int, 2> default_stick_mod; 44 static const std::array<int, 2> default_stick_mod;
45 static const std::array<int, 2> default_ringcon_analogs;
45 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 46 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
46 default_mouse_buttons; 47 default_mouse_buttons;
47 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys; 48 static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -66,6 +67,7 @@ private:
66 void ReadMouseValues(); 67 void ReadMouseValues();
67 void ReadTouchscreenValues(); 68 void ReadTouchscreenValues();
68 void ReadMotionTouchValues(); 69 void ReadMotionTouchValues();
70 void ReadHidbusValues();
69 71
70 // Read functions bases off the respective config section names. 72 // Read functions bases off the respective config section names.
71 void ReadAudioValues(); 73 void ReadAudioValues();
@@ -93,6 +95,7 @@ private:
93 void SaveMouseValues(); 95 void SaveMouseValues();
94 void SaveTouchscreenValues(); 96 void SaveTouchscreenValues();
95 void SaveMotionTouchValues(); 97 void SaveMotionTouchValues();
98 void SaveHidbusValues();
96 99
97 // Save functions based off the respective config section names. 100 // Save functions based off the respective config section names.
98 void SaveAudioValues(); 101 void SaveAudioValues();
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 4ca74a5f7..73d7ba24b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -20,6 +20,7 @@
20#include "yuzu/configuration/configure_input_advanced.h" 20#include "yuzu/configuration/configure_input_advanced.h"
21#include "yuzu/configuration/configure_input_player.h" 21#include "yuzu/configuration/configure_input_player.h"
22#include "yuzu/configuration/configure_motion_touch.h" 22#include "yuzu/configuration/configure_motion_touch.h"
23#include "yuzu/configuration/configure_ringcon.h"
23#include "yuzu/configuration/configure_touchscreen_advanced.h" 24#include "yuzu/configuration/configure_touchscreen_advanced.h"
24#include "yuzu/configuration/configure_vibration.h" 25#include "yuzu/configuration/configure_vibration.h"
25#include "yuzu/configuration/input_profiles.h" 26#include "yuzu/configuration/input_profiles.h"
@@ -158,6 +159,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
158 [this, input_subsystem] { 159 [this, input_subsystem] {
159 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 160 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
160 }); 161 });
162 connect(advanced, &ConfigureInputAdvanced::CallRingControllerDialog,
163 [this, input_subsystem, &hid_core] {
164 CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
165 });
161 166
162 connect(ui->vibrationButton, &QPushButton::clicked, 167 connect(ui->vibrationButton, &QPushButton::clicked,
163 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); }); 168 [this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 20fc2599d..8fd1f4a38 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -79,13 +79,17 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
79 &ConfigureInputAdvanced::UpdateUIEnabled); 79 &ConfigureInputAdvanced::UpdateUIEnabled);
80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this, 80 connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
81 &ConfigureInputAdvanced::UpdateUIEnabled); 81 &ConfigureInputAdvanced::UpdateUIEnabled);
82 connect(ui->enable_ring_controller, &QCheckBox::stateChanged, this,
83 &ConfigureInputAdvanced::UpdateUIEnabled);
82 84
83 connect(ui->debug_configure, &QPushButton::clicked, this, 85 connect(ui->debug_configure, &QPushButton::clicked, this,
84 [this] { CallDebugControllerDialog(); }); 86 [this] { CallDebugControllerDialog(); });
85 connect(ui->touchscreen_advanced, &QPushButton::clicked, this, 87 connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
86 [this] { CallTouchscreenConfigDialog(); }); 88 [this] { CallTouchscreenConfigDialog(); });
87 connect(ui->buttonMotionTouch, &QPushButton::clicked, this, 89 connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
88 &ConfigureInputAdvanced::CallMotionTouchConfigDialog); 90 [this] { CallMotionTouchConfigDialog(); });
91 connect(ui->ring_controller_configure, &QPushButton::clicked, this,
92 [this] { CallRingControllerDialog(); });
89 93
90#ifndef _WIN32 94#ifndef _WIN32
91 ui->enable_raw_input->setVisible(false); 95 ui->enable_raw_input->setVisible(false);
@@ -132,6 +136,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
132 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked(); 136 Settings::values.enable_raw_input = ui->enable_raw_input->isChecked();
133 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked(); 137 Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
134 Settings::values.controller_navigation = ui->controller_navigation->isChecked(); 138 Settings::values.controller_navigation = ui->controller_navigation->isChecked();
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
135} 140}
136 141
137void ConfigureInputAdvanced::LoadConfiguration() { 142void ConfigureInputAdvanced::LoadConfiguration() {
@@ -164,6 +169,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
164 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue()); 169 ui->enable_raw_input->setChecked(Settings::values.enable_raw_input.GetValue());
165 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue()); 170 ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
166 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue()); 171 ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
172 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
167 173
168 UpdateUIEnabled(); 174 UpdateUIEnabled();
169} 175}
@@ -185,4 +191,5 @@ void ConfigureInputAdvanced::UpdateUIEnabled() {
185 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked()); 191 ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
186 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked()); 192 ui->mouse_panning->setEnabled(!ui->mouse_enabled->isChecked());
187 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked()); 193 ui->mouse_panning_sensitivity->setEnabled(!ui->mouse_enabled->isChecked());
194 ui->ring_controller_configure->setEnabled(ui->enable_ring_controller->isChecked());
188} 195}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
index 3083d55c1..4472cb846 100644
--- a/src/yuzu/configuration/configure_input_advanced.h
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -29,6 +29,7 @@ signals:
29 void CallMouseConfigDialog(); 29 void CallMouseConfigDialog();
30 void CallTouchscreenConfigDialog(); 30 void CallTouchscreenConfigDialog();
31 void CallMotionTouchConfigDialog(); 31 void CallMotionTouchConfigDialog();
32 void CallRingControllerDialog();
32 33
33private: 34private:
34 void changeEvent(QEvent* event) override; 35 void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 66f2075f2..14403cb10 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2603,6 +2603,20 @@
2603 </property> 2603 </property>
2604 </widget> 2604 </widget>
2605 </item> 2605 </item>
2606 <item row="4" column="0">
2607 <widget class="QCheckBox" name="enable_ring_controller">
2608 <property name="text">
2609 <string>Ring Controller</string>
2610 </property>
2611 </widget>
2612 </item>
2613 <item row="4" column="2">
2614 <widget class="QPushButton" name="ring_controller_configure">
2615 <property name="text">
2616 <string>Configure</string>
2617 </property>
2618 </widget>
2619 </item>
2606 </layout> 2620 </layout>
2607 </widget> 2621 </widget>
2608 </item> 2622 </item>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 8ef3596dd..291e37acf 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -1407,10 +1407,10 @@ void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
1407} 1407}
1408 1408
1409void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) { 1409void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
1410 event->ignore();
1411 if (!input_setter || !event) { 1410 if (!input_setter || !event) {
1412 return; 1411 return;
1413 } 1412 }
1413 event->ignore();
1414 if (event->key() != Qt::Key_Escape) { 1414 if (event->key() != Qt::Key_Escape) {
1415 input_subsystem->GetKeyboard()->PressKey(event->key()); 1415 input_subsystem->GetKeyboard()->PressKey(event->key());
1416 } 1416 }
diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp
new file mode 100644
index 000000000..f1cdd4fed
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <QKeyEvent>
7#include <QMenu>
8#include <QTimer>
9
10#include "core/hid/emulated_devices.h"
11#include "core/hid/hid_core.h"
12#include "input_common/drivers/keyboard.h"
13#include "input_common/drivers/mouse.h"
14#include "input_common/main.h"
15#include "ui_configure_ringcon.h"
16#include "yuzu/bootmanager.h"
17#include "yuzu/configuration/config.h"
18#include "yuzu/configuration/configure_ringcon.h"
19
20const std::array<std::string, ConfigureRingController::ANALOG_SUB_BUTTONS_NUM>
21 ConfigureRingController::analog_sub_buttons{{
22 "left",
23 "right",
24 }};
25
26namespace {
27
28QString GetKeyName(int key_code) {
29 switch (key_code) {
30 case Qt::Key_Shift:
31 return QObject::tr("Shift");
32 case Qt::Key_Control:
33 return QObject::tr("Ctrl");
34 case Qt::Key_Alt:
35 return QObject::tr("Alt");
36 case Qt::Key_Meta:
37 return {};
38 default:
39 return QKeySequence(key_code).toString();
40 }
41}
42
43QString GetButtonName(Common::Input::ButtonNames button_name) {
44 switch (button_name) {
45 case Common::Input::ButtonNames::ButtonLeft:
46 return QObject::tr("Left");
47 case Common::Input::ButtonNames::ButtonRight:
48 return QObject::tr("Right");
49 case Common::Input::ButtonNames::ButtonDown:
50 return QObject::tr("Down");
51 case Common::Input::ButtonNames::ButtonUp:
52 return QObject::tr("Up");
53 case Common::Input::ButtonNames::TriggerZ:
54 return QObject::tr("Z");
55 case Common::Input::ButtonNames::TriggerR:
56 return QObject::tr("R");
57 case Common::Input::ButtonNames::TriggerL:
58 return QObject::tr("L");
59 case Common::Input::ButtonNames::ButtonA:
60 return QObject::tr("A");
61 case Common::Input::ButtonNames::ButtonB:
62 return QObject::tr("B");
63 case Common::Input::ButtonNames::ButtonX:
64 return QObject::tr("X");
65 case Common::Input::ButtonNames::ButtonY:
66 return QObject::tr("Y");
67 case Common::Input::ButtonNames::ButtonStart:
68 return QObject::tr("Start");
69 case Common::Input::ButtonNames::L1:
70 return QObject::tr("L1");
71 case Common::Input::ButtonNames::L2:
72 return QObject::tr("L2");
73 case Common::Input::ButtonNames::L3:
74 return QObject::tr("L3");
75 case Common::Input::ButtonNames::R1:
76 return QObject::tr("R1");
77 case Common::Input::ButtonNames::R2:
78 return QObject::tr("R2");
79 case Common::Input::ButtonNames::R3:
80 return QObject::tr("R3");
81 case Common::Input::ButtonNames::Circle:
82 return QObject::tr("Circle");
83 case Common::Input::ButtonNames::Cross:
84 return QObject::tr("Cross");
85 case Common::Input::ButtonNames::Square:
86 return QObject::tr("Square");
87 case Common::Input::ButtonNames::Triangle:
88 return QObject::tr("Triangle");
89 case Common::Input::ButtonNames::Share:
90 return QObject::tr("Share");
91 case Common::Input::ButtonNames::Options:
92 return QObject::tr("Options");
93 default:
94 return QObject::tr("[undefined]");
95 }
96}
97
98void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
99 const std::string& button_name) {
100 // The poller returned a complete axis, so set all the buttons
101 if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
102 analog_param = input_param;
103 return;
104 }
105 // Check if the current configuration has either no engine or an axis binding.
106 // Clears out the old binding and adds one with analog_from_button.
107 if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
108 analog_param = {
109 {"engine", "analog_from_button"},
110 };
111 }
112 analog_param.Set(button_name, input_param.Serialize());
113}
114} // namespace
115
116ConfigureRingController::ConfigureRingController(QWidget* parent,
117 InputCommon::InputSubsystem* input_subsystem_,
118 Core::HID::HIDCore& hid_core_)
119 : QDialog(parent), timeout_timer(std::make_unique<QTimer>()),
120 poll_timer(std::make_unique<QTimer>()), input_subsystem{input_subsystem_},
121
122 ui(std::make_unique<Ui::ConfigureRingController>()) {
123 ui->setupUi(this);
124
125 analog_map_buttons = {
126 ui->buttonRingAnalogPull,
127 ui->buttonRingAnalogPush,
128 };
129
130 emulated_device = hid_core_.GetEmulatedDevices();
131 emulated_device->SaveCurrentConfig();
132 emulated_device->EnableConfiguration();
133
134 LoadConfiguration();
135
136 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
137 auto* const analog_button = analog_map_buttons[sub_button_id];
138
139 if (analog_button == nullptr) {
140 continue;
141 }
142
143 connect(analog_button, &QPushButton::clicked, [=, this] {
144 HandleClick(
145 analog_map_buttons[sub_button_id],
146 [=, this](const Common::ParamPackage& params) {
147 Common::ParamPackage param = emulated_device->GetRingParam();
148 SetAnalogParam(params, param, analog_sub_buttons[sub_button_id]);
149 emulated_device->SetRingParam(param);
150 },
151 InputCommon::Polling::InputType::Stick);
152 });
153
154 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
155
156 connect(analog_button, &QPushButton::customContextMenuRequested,
157 [=, this](const QPoint& menu_location) {
158 QMenu context_menu;
159 Common::ParamPackage param = emulated_device->GetRingParam();
160 context_menu.addAction(tr("Clear"), [&] {
161 emulated_device->SetRingParam({});
162 analog_map_buttons[sub_button_id]->setText(tr("[not set]"));
163 });
164 context_menu.addAction(tr("Invert axis"), [&] {
165 const bool invert_value = param.Get("invert_x", "+") == "-";
166 const std::string invert_str = invert_value ? "+" : "-";
167 param.Set("invert_x", invert_str);
168 emulated_device->SetRingParam(param);
169 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM;
170 ++sub_button_id) {
171 analog_map_buttons[sub_button_id]->setText(
172 AnalogToText(param, analog_sub_buttons[sub_button_id]));
173 }
174 });
175 context_menu.exec(
176 analog_map_buttons[sub_button_id]->mapToGlobal(menu_location));
177 });
178 }
179
180 connect(ui->sliderRingAnalogDeadzone, &QSlider::valueChanged, [=, this] {
181 Common::ParamPackage param = emulated_device->GetRingParam();
182 const auto slider_value = ui->sliderRingAnalogDeadzone->value();
183 ui->labelRingAnalogDeadzone->setText(tr("Deadzone: %1%").arg(slider_value));
184 param.Set("deadzone", slider_value / 100.0f);
185 emulated_device->SetRingParam(param);
186 });
187
188 connect(ui->restore_defaults_button, &QPushButton::clicked, this,
189 &ConfigureRingController::RestoreDefaults);
190
191 timeout_timer->setSingleShot(true);
192 connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
193
194 connect(poll_timer.get(), &QTimer::timeout, [this] {
195 const auto& params = input_subsystem->GetNextInput();
196 if (params.Has("engine") && IsInputAcceptable(params)) {
197 SetPollingResult(params, false);
198 return;
199 }
200 });
201
202 resize(0, 0);
203}
204
205ConfigureRingController::~ConfigureRingController() {
206 emulated_device->DisableConfiguration();
207};
208
209void ConfigureRingController::changeEvent(QEvent* event) {
210 if (event->type() == QEvent::LanguageChange) {
211 RetranslateUI();
212 }
213
214 QDialog::changeEvent(event);
215}
216
217void ConfigureRingController::RetranslateUI() {
218 ui->retranslateUi(this);
219}
220
221void ConfigureRingController::UpdateUI() {
222 RetranslateUI();
223 const Common::ParamPackage param = emulated_device->GetRingParam();
224
225 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
226 auto* const analog_button = analog_map_buttons[sub_button_id];
227
228 if (analog_button == nullptr) {
229 continue;
230 }
231
232 analog_button->setText(AnalogToText(param, analog_sub_buttons[sub_button_id]));
233 }
234
235 const auto deadzone_label = ui->labelRingAnalogDeadzone;
236 const auto deadzone_slider = ui->sliderRingAnalogDeadzone;
237
238 int slider_value = static_cast<int>(param.Get("deadzone", 0.15f) * 100);
239 deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
240 deadzone_slider->setValue(slider_value);
241}
242
243void ConfigureRingController::ApplyConfiguration() {
244 emulated_device->DisableConfiguration();
245 emulated_device->SaveCurrentConfig();
246 emulated_device->EnableConfiguration();
247}
248
249void ConfigureRingController::LoadConfiguration() {
250 UpdateUI();
251}
252
253void ConfigureRingController::RestoreDefaults() {
254 const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys(
255 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f);
256 emulated_device->SetRingParam(Common::ParamPackage(default_ring_string));
257 UpdateUI();
258}
259
260void ConfigureRingController::HandleClick(
261 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
262 InputCommon::Polling::InputType type) {
263 button->setText(tr("[waiting]"));
264 button->setFocus();
265
266 input_setter = new_input_setter;
267
268 input_subsystem->BeginMapping(type);
269
270 QWidget::grabMouse();
271 QWidget::grabKeyboard();
272
273 timeout_timer->start(2500); // Cancel after 2.5 seconds
274 poll_timer->start(25); // Check for new inputs every 25ms
275}
276
277void ConfigureRingController::SetPollingResult(const Common::ParamPackage& params, bool abort) {
278 timeout_timer->stop();
279 poll_timer->stop();
280 input_subsystem->StopMapping();
281
282 QWidget::releaseMouse();
283 QWidget::releaseKeyboard();
284
285 if (!abort) {
286 (*input_setter)(params);
287 }
288
289 UpdateUI();
290
291 input_setter = std::nullopt;
292}
293
294bool ConfigureRingController::IsInputAcceptable(const Common::ParamPackage& params) const {
295 return true;
296}
297
298void ConfigureRingController::mousePressEvent(QMouseEvent* event) {
299 if (!input_setter || !event) {
300 return;
301 }
302
303 const auto button = GRenderWindow::QtButtonToMouseButton(event->button());
304 input_subsystem->GetMouse()->PressButton(0, 0, 0, 0, button);
305}
306
307void ConfigureRingController::keyPressEvent(QKeyEvent* event) {
308 if (!input_setter || !event) {
309 return;
310 }
311 event->ignore();
312 if (event->key() != Qt::Key_Escape) {
313 input_subsystem->GetKeyboard()->PressKey(event->key());
314 }
315}
316
317QString ConfigureRingController::ButtonToText(const Common::ParamPackage& param) {
318 if (!param.Has("engine")) {
319 return QObject::tr("[not set]");
320 }
321
322 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
323 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
324 const auto common_button_name = input_subsystem->GetButtonName(param);
325
326 // Retrieve the names from Qt
327 if (param.Get("engine", "") == "keyboard") {
328 const QString button_str = GetKeyName(param.Get("code", 0));
329 return QObject::tr("%1%2").arg(toggle, button_str);
330 }
331
332 if (common_button_name == Common::Input::ButtonNames::Invalid) {
333 return QObject::tr("[invalid]");
334 }
335
336 if (common_button_name == Common::Input::ButtonNames::Engine) {
337 return QString::fromStdString(param.Get("engine", ""));
338 }
339
340 if (common_button_name == Common::Input::ButtonNames::Value) {
341 if (param.Has("hat")) {
342 const QString hat = QString::fromStdString(param.Get("direction", ""));
343 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat);
344 }
345 if (param.Has("axis")) {
346 const QString axis = QString::fromStdString(param.Get("axis", ""));
347 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, axis);
348 }
349 if (param.Has("axis_x") && param.Has("axis_y") && param.Has("axis_z")) {
350 const QString axis_x = QString::fromStdString(param.Get("axis_x", ""));
351 const QString axis_y = QString::fromStdString(param.Get("axis_y", ""));
352 const QString axis_z = QString::fromStdString(param.Get("axis_z", ""));
353 return QObject::tr("%1%2Axis %3,%4,%5").arg(toggle, inverted, axis_x, axis_y, axis_z);
354 }
355 if (param.Has("motion")) {
356 const QString motion = QString::fromStdString(param.Get("motion", ""));
357 return QObject::tr("%1%2Motion %3").arg(toggle, inverted, motion);
358 }
359 if (param.Has("button")) {
360 const QString button = QString::fromStdString(param.Get("button", ""));
361 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button);
362 }
363 }
364
365 QString button_name = GetButtonName(common_button_name);
366 if (param.Has("hat")) {
367 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name);
368 }
369 if (param.Has("axis")) {
370 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
371 }
372 if (param.Has("motion")) {
373 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
374 }
375 if (param.Has("button")) {
376 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name);
377 }
378
379 return QObject::tr("[unknown]");
380}
381
382QString ConfigureRingController::AnalogToText(const Common::ParamPackage& param,
383 const std::string& dir) {
384 if (!param.Has("engine")) {
385 return QObject::tr("[not set]");
386 }
387
388 if (param.Get("engine", "") == "analog_from_button") {
389 return ButtonToText(Common::ParamPackage{param.Get(dir, "")});
390 }
391
392 if (!param.Has("axis_x") || !param.Has("axis_y")) {
393 return QObject::tr("[unknown]");
394 }
395
396 const auto engine_str = param.Get("engine", "");
397 const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
398 const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
399 const bool invert_x = param.Get("invert_x", "+") == "-";
400 const bool invert_y = param.Get("invert_y", "+") == "-";
401
402 if (dir == "modifier") {
403 return QObject::tr("[unused]");
404 }
405
406 if (dir == "left") {
407 const QString invert_x_str = QString::fromStdString(invert_x ? "+" : "-");
408 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
409 }
410 if (dir == "right") {
411 const QString invert_x_str = QString::fromStdString(invert_x ? "-" : "+");
412 return QObject::tr("Axis %1%2").arg(axis_x_str, invert_x_str);
413 }
414 if (dir == "up") {
415 const QString invert_y_str = QString::fromStdString(invert_y ? "-" : "+");
416 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
417 }
418 if (dir == "down") {
419 const QString invert_y_str = QString::fromStdString(invert_y ? "+" : "-");
420 return QObject::tr("Axis %1%2").arg(axis_y_str, invert_y_str);
421 }
422
423 return QObject::tr("[unknown]");
424} \ No newline at end of file
diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h
new file mode 100644
index 000000000..cf9e54f09
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.h
@@ -0,0 +1,85 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9#include <QDialog>
10
11namespace InputCommon {
12class InputSubsystem;
13} // namespace InputCommon
14
15namespace Core::HID {
16class HIDCore;
17class EmulatedDevices;
18} // namespace Core::HID
19
20namespace Ui {
21class ConfigureRingController;
22} // namespace Ui
23
24class ConfigureRingController : public QDialog {
25 Q_OBJECT
26
27public:
28 explicit ConfigureRingController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_,
29 Core::HID::HIDCore& hid_core_);
30 ~ConfigureRingController() override;
31
32 void ApplyConfiguration();
33
34private:
35 void changeEvent(QEvent* event) override;
36 void RetranslateUI();
37
38 void UpdateUI();
39
40 /// Load configuration settings.
41 void LoadConfiguration();
42
43 /// Restore all buttons to their default values.
44 void RestoreDefaults();
45
46 /// Called when the button was pressed.
47 void HandleClick(QPushButton* button,
48 std::function<void(const Common::ParamPackage&)> new_input_setter,
49 InputCommon::Polling::InputType type);
50
51 /// Finish polling and configure input using the input_setter.
52 void SetPollingResult(const Common::ParamPackage& params, bool abort);
53
54 /// Checks whether a given input can be accepted.
55 bool IsInputAcceptable(const Common::ParamPackage& params) const;
56
57 /// Handle mouse button press events.
58 void mousePressEvent(QMouseEvent* event) override;
59
60 /// Handle key press events.
61 void keyPressEvent(QKeyEvent* event) override;
62
63 QString ButtonToText(const Common::ParamPackage& param);
64
65 QString AnalogToText(const Common::ParamPackage& param, const std::string& dir);
66
67 static constexpr int ANALOG_SUB_BUTTONS_NUM = 2;
68
69 // A group of four QPushButtons represent one analog input. The buttons each represent left,
70 // right, respectively.
71 std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM> analog_map_buttons;
72
73 static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
74
75 std::unique_ptr<QTimer> timeout_timer;
76 std::unique_ptr<QTimer> poll_timer;
77
78 /// This will be the the setting function when an input is awaiting configuration.
79 std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
80
81 InputCommon::InputSubsystem* input_subsystem;
82 Core::HID::EmulatedDevices* emulated_device;
83
84 std::unique_ptr<Ui::ConfigureRingController> ui;
85};
diff --git a/src/yuzu/configuration/configure_ringcon.ui b/src/yuzu/configuration/configure_ringcon.ui
new file mode 100644
index 000000000..9ec634dd4
--- /dev/null
+++ b/src/yuzu/configuration/configure_ringcon.ui
@@ -0,0 +1,278 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureRingController</class>
4 <widget class="QDialog" name="ConfigureRingController">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>298</width>
10 <height>339</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Configure Ring Controller</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <widget class="QLabel" name="label_2">
19 <property name="minimumSize">
20 <size>
21 <width>280</width>
22 <height>0</height>
23 </size>
24 </property>
25 <property name="text">
26 <string>If you want to use this controller configure player 1 as right controller and player 2 as dual joycon before starting the game to allow this controller to be detected properly.</string>
27 </property>
28 <property name="wordWrap">
29 <bool>true</bool>
30 </property>
31 </widget>
32 </item>
33 <item>
34 <spacer name="verticalSpacer_2">
35 <property name="orientation">
36 <enum>Qt::Vertical</enum>
37 </property>
38 <property name="sizeType">
39 <enum>QSizePolicy::Fixed</enum>
40 </property>
41 <property name="sizeHint" stdset="0">
42 <size>
43 <width>20</width>
44 <height>10</height>
45 </size>
46 </property>
47 </spacer>
48 </item>
49 <item>
50 <widget class="QGroupBox" name="RingAnalog">
51 <property name="title">
52 <string>Ring Sensor Parameters</string>
53 </property>
54 <property name="alignment">
55 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
56 </property>
57 <layout class="QVBoxLayout" name="verticalLayout_3">
58 <property name="spacing">
59 <number>0</number>
60 </property>
61 <property name="sizeConstraint">
62 <enum>QLayout::SetDefaultConstraint</enum>
63 </property>
64 <property name="leftMargin">
65 <number>3</number>
66 </property>
67 <property name="topMargin">
68 <number>6</number>
69 </property>
70 <property name="rightMargin">
71 <number>3</number>
72 </property>
73 <property name="bottomMargin">
74 <number>0</number>
75 </property>
76 <item>
77 <layout class="QHBoxLayout" name="buttonRingAnalogPullHorizontaLayout">
78 <property name="spacing">
79 <number>3</number>
80 </property>
81 <item alignment="Qt::AlignHCenter">
82 <widget class="QGroupBox" name="buttonRingAnalogPullGroup">
83 <property name="title">
84 <string>Pull</string>
85 </property>
86 <property name="alignment">
87 <set>Qt::AlignCenter</set>
88 </property>
89 <layout class="QVBoxLayout" name="buttonRingAnalogPullVerticalLayout">
90 <property name="spacing">
91 <number>3</number>
92 </property>
93 <property name="leftMargin">
94 <number>3</number>
95 </property>
96 <property name="topMargin">
97 <number>3</number>
98 </property>
99 <property name="rightMargin">
100 <number>3</number>
101 </property>
102 <property name="bottomMargin">
103 <number>3</number>
104 </property>
105 <item>
106 <widget class="QPushButton" name="buttonRingAnalogPull">
107 <property name="minimumSize">
108 <size>
109 <width>68</width>
110 <height>0</height>
111 </size>
112 </property>
113 <property name="maximumSize">
114 <size>
115 <width>68</width>
116 <height>16777215</height>
117 </size>
118 </property>
119 <property name="styleSheet">
120 <string notr="true">min-width: 68px;</string>
121 </property>
122 <property name="text">
123 <string>Pull</string>
124 </property>
125 </widget>
126 </item>
127 </layout>
128 </widget>
129 </item>
130 <item alignment="Qt::AlignHCenter">
131 <widget class="QGroupBox" name="buttonRingAnalogPushGroup">
132 <property name="title">
133 <string>Push</string>
134 </property>
135 <property name="alignment">
136 <set>Qt::AlignCenter</set>
137 </property>
138 <layout class="QVBoxLayout" name="buttonRingAnalogPushVerticalLayout">
139 <property name="spacing">
140 <number>3</number>
141 </property>
142 <property name="leftMargin">
143 <number>3</number>
144 </property>
145 <property name="topMargin">
146 <number>3</number>
147 </property>
148 <property name="rightMargin">
149 <number>3</number>
150 </property>
151 <property name="bottomMargin">
152 <number>3</number>
153 </property>
154 <item>
155 <widget class="QPushButton" name="buttonRingAnalogPush">
156 <property name="minimumSize">
157 <size>
158 <width>68</width>
159 <height>0</height>
160 </size>
161 </property>
162 <property name="maximumSize">
163 <size>
164 <width>68</width>
165 <height>16777215</height>
166 </size>
167 </property>
168 <property name="styleSheet">
169 <string notr="true">min-width: 68px;</string>
170 </property>
171 <property name="text">
172 <string>Push</string>
173 </property>
174 </widget>
175 </item>
176 </layout>
177 </widget>
178 </item>
179 </layout>
180 </item>
181 <item>
182 <layout class="QVBoxLayout" name="sliderRingAnalogDeadzoneVerticalLayout">
183 <property name="spacing">
184 <number>3</number>
185 </property>
186 <property name="sizeConstraint">
187 <enum>QLayout::SetDefaultConstraint</enum>
188 </property>
189 <property name="leftMargin">
190 <number>0</number>
191 </property>
192 <property name="topMargin">
193 <number>10</number>
194 </property>
195 <property name="rightMargin">
196 <number>0</number>
197 </property>
198 <property name="bottomMargin">
199 <number>3</number>
200 </property>
201 <item>
202 <layout class="QHBoxLayout" name="sliderRingAnalogDeadzoneHorizontalLayout">
203 <item>
204 <widget class="QLabel" name="labelRingAnalogDeadzone">
205 <property name="text">
206 <string>Deadzone: 0%</string>
207 </property>
208 <property name="alignment">
209 <set>Qt::AlignHCenter</set>
210 </property>
211 </widget>
212 </item>
213 </layout>
214 </item>
215 <item>
216 <widget class="QSlider" name="sliderRingAnalogDeadzone">
217 <property name="maximum">
218 <number>100</number>
219 </property>
220 <property name="orientation">
221 <enum>Qt::Horizontal</enum>
222 </property>
223 </widget>
224 </item>
225 </layout>
226 </item>
227 </layout>
228 </widget>
229 </item>
230 <item>
231 <spacer name="verticalSpacer">
232 <property name="orientation">
233 <enum>Qt::Vertical</enum>
234 </property>
235 <property name="sizeHint" stdset="0">
236 <size>
237 <width>20</width>
238 <height>40</height>
239 </size>
240 </property>
241 </spacer>
242 </item>
243 <item>
244 <layout class="QHBoxLayout" name="horizontalLayout">
245 <item>
246 <widget class="QPushButton" name="restore_defaults_button">
247 <property name="text">
248 <string>Restore Defaults</string>
249 </property>
250 </widget>
251 </item>
252 <item>
253 <widget class="QDialogButtonBox" name="buttonBox">
254 <property name="standardButtons">
255 <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
256 </property>
257 </widget>
258 </item>
259 </layout>
260 </item>
261 </layout>
262 </widget>
263 <resources/>
264 <connections>
265 <connection>
266 <sender>buttonBox</sender>
267 <signal>accepted()</signal>
268 <receiver>ConfigureRingController</receiver>
269 <slot>accept()</slot>
270 </connection>
271 <connection>
272 <sender>buttonBox</sender>
273 <signal>rejected()</signal>
274 <receiver>ConfigureRingController</receiver>
275 <slot>reject()</slot>
276 </connection>
277 </connections>
278</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 62d15f8cd..52879a989 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3652,6 +3652,14 @@ void GMainWindow::UpdateUITheme() {
3652 setStyleSheet({}); 3652 setStyleSheet({});
3653 } 3653 }
3654 3654
3655 QPalette new_pal(qApp->palette());
3656 if (UISettings::IsDarkTheme()) {
3657 new_pal.setColor(QPalette::Link, QColor(0, 190, 255, 255));
3658 } else {
3659 new_pal.setColor(QPalette::Link, QColor(0, 140, 200, 255));
3660 }
3661 qApp->setPalette(new_pal);
3662
3655 QIcon::setThemeName(current_theme); 3663 QIcon::setThemeName(current_theme);
3656 QIcon::setThemeSearchPaths(theme_paths); 3664 QIcon::setThemeSearchPaths(theme_paths);
3657} 3665}
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 21683576c..f683b80f7 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -15,6 +15,14 @@ const Themes themes{{
15 {"Midnight Blue Colorful", "colorful_midnight_blue"}, 15 {"Midnight Blue Colorful", "colorful_midnight_blue"},
16}}; 16}};
17 17
18bool IsDarkTheme() {
19 const auto& theme = UISettings::values.theme;
20 return theme == QStringLiteral("qdarkstyle") ||
21 theme == QStringLiteral("qdarkstyle_midnight_blue") ||
22 theme == QStringLiteral("colorful_dark") ||
23 theme == QStringLiteral("colorful_midnight_blue");
24}
25
18Values values = {}; 26Values values = {};
19 27
20} // namespace UISettings 28} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index cc5aee382..15ba9ea17 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -17,6 +17,8 @@
17 17
18namespace UISettings { 18namespace UISettings {
19 19
20bool IsDarkTheme();
21
20struct ContextualShortcut { 22struct ContextualShortcut {
21 QString keyseq; 23 QString keyseq;
22 QString controller_keyseq; 24 QString controller_keyseq;
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 57f807826..ae2e62dc5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -123,14 +123,15 @@ void EmuWindow_SDL2::ShowCursor(bool show_cursor) {
123} 123}
124 124
125void EmuWindow_SDL2::Fullscreen() { 125void EmuWindow_SDL2::Fullscreen() {
126 SDL_DisplayMode display_mode;
126 switch (Settings::values.fullscreen_mode.GetValue()) { 127 switch (Settings::values.fullscreen_mode.GetValue()) {
127 case Settings::FullscreenMode::Exclusive: 128 case Settings::FullscreenMode::Exclusive:
128 // Set window size to render size before entering fullscreen -- SDL does not resize to 129 // Set window size to render size before entering fullscreen -- SDL2 does not resize window
129 // display dimensions in this mode. 130 // to display dimensions automatically in this mode.
130 // TODO: Multiply the window size by resolution_factor (for both docked modes) 131 if (SDL_GetDesktopDisplayMode(0, &display_mode) == 0) {
131 if (Settings::values.use_docked_mode) { 132 SDL_SetWindowSize(render_window, display_mode.w, display_mode.h);
132 SDL_SetWindowSize(render_window, Layout::ScreenDocked::Width, 133 } else {
133 Layout::ScreenDocked::Height); 134 LOG_ERROR(Frontend, "SDL_GetDesktopDisplayMode failed: {}", SDL_GetError());
134 } 135 }
135 136
136 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { 137 if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) {