summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/command_generator.cpp23
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h4
-rw-r--r--src/core/core.cpp8
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/crypto/key_manager.cpp7
-rw-r--r--src/core/crypto/key_manager.h6
-rw-r--r--src/core/file_sys/bis_factory.cpp10
-rw-r--r--src/core/file_sys/bis_factory.h2
-rw-r--r--src/core/file_sys/control_metadata.h2
-rw-r--r--src/core/file_sys/nca_patch.cpp83
-rw-r--r--src/core/file_sys/nca_patch.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp138
-rw-r--r--src/core/file_sys/patch_manager.h48
-rw-r--r--src/core/file_sys/romfs_factory.cpp50
-rw-r--r--src/core/file_sys/romfs_factory.h21
-rw-r--r--src/core/frontend/input.h22
-rw-r--r--src/core/hle/kernel/client_session.cpp5
-rw-r--r--src/core/hle/kernel/client_session.h7
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/server_session.h11
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp10
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp106
-rw-r--r--src/core/hle/service/hid/controllers/npad.h54
-rw-r--r--src/core/hle/service/hid/hid.cpp39
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp23
-rw-r--r--src/core/hle/service/service.cpp28
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/sm/sm.cpp13
-rw-r--r--src/core/hle/service/sm/sm.h7
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp18
-rw-r--r--src/core/loader/deconstructed_rom_directory.h6
-rw-r--r--src/core/loader/elf.cpp3
-rw-r--r--src/core/loader/elf.h6
-rw-r--r--src/core/loader/kip.cpp3
-rw-r--r--src/core/loader/kip.h6
-rw-r--r--src/core/loader/loader.h7
-rw-r--r--src/core/loader/nax.cpp5
-rw-r--r--src/core/loader/nax.h8
-rw-r--r--src/core/loader/nca.cpp8
-rw-r--r--src/core/loader/nca.h6
-rw-r--r--src/core/loader/nro.cpp6
-rw-r--r--src/core/loader/nro.h6
-rw-r--r--src/core/loader/nso.cpp7
-rw-r--r--src/core/loader/nso.h12
-rw-r--r--src/core/loader/nsp.cpp7
-rw-r--r--src/core/loader/nsp.h6
-rw-r--r--src/core/loader/xci.cpp7
-rw-r--r--src/core/loader/xci.h6
-rw-r--r--src/core/memory/cheat_engine.cpp41
-rw-r--r--src/core/memory/cheat_engine.h5
-rw-r--r--src/core/settings.h1
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp9
-rw-r--r--src/input_common/main.cpp46
-rw-r--r--src/input_common/main.h24
-rw-r--r--src/input_common/motion_emu.cpp17
-rw-r--r--src/input_common/settings.cpp7
-rw-r--r--src/input_common/settings.h17
-rw-r--r--src/input_common/udp/client.cpp176
-rw-r--r--src/input_common/udp/client.h77
-rw-r--r--src/input_common/udp/udp.cpp181
-rw-r--r--src/input_common/udp/udp.h61
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/engines/fermi_2d.cpp12
-rw-r--r--src/video_core/engines/fermi_2d.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp4
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h6
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp24
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp16
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp3
-rw-r--r--src/video_core/shader/decode/image.cpp15
-rw-r--r--src/video_core/shader/decode/texture.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp31
-rw-r--r--src/yuzu/configuration/config.h1
-rw-r--r--src/yuzu/configuration/configure_input.cpp7
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp195
-rw-r--r--src/yuzu/configuration/configure_input_player.h10
-rw-r--r--src/yuzu/configuration/configure_input_player.ui109
-rw-r--r--src/yuzu/main.cpp6
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_tester/config.cpp1
84 files changed, 1469 insertions, 529 deletions
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index 84782cde6..8f7da49e6 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -196,7 +196,7 @@ void CommandGenerator::PreCommand() {
196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) { 196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
197 const auto& base = splitter_context.GetInfo(i); 197 const auto& base = splitter_context.GetInfo(i);
198 std::string graph = fmt::format("b[{}]", i); 198 std::string graph = fmt::format("b[{}]", i);
199 auto* head = base.GetHead(); 199 const auto* head = base.GetHead();
200 while (head != nullptr) { 200 while (head != nullptr) {
201 graph += fmt::format("->{}", head->GetMixId()); 201 graph += fmt::format("->{}", head->GetMixId());
202 head = head->GetNextDestination(); 202 head = head->GetNextDestination();
@@ -214,7 +214,7 @@ void CommandGenerator::PostCommand() {
214 214
215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
216 s32 channel) { 216 s32 channel) {
217 auto& in_params = voice_info.GetInParams(); 217 const auto& in_params = voice_info.GetInParams();
218 const auto depop = in_params.should_depop; 218 const auto depop = in_params.should_depop;
219 219
220 if (depop) { 220 if (depop) {
@@ -405,7 +405,7 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset,
405} 405}
406 406
407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) { 407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
408 auto aux = dynamic_cast<EffectAuxInfo*>(info); 408 auto* aux = dynamic_cast<EffectAuxInfo*>(info);
409 const auto& params = aux->GetParams(); 409 const auto& params = aux->GetParams();
410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) { 410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
411 const auto max_channels = params.count; 411 const auto max_channels = params.count;
@@ -571,7 +571,7 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
571 if (dumping_frame) { 571 if (dumping_frame) {
572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand"); 572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
573 } 573 }
574 auto& in_params = mix_info.GetInParams(); 574 const auto& in_params = mix_info.GetInParams();
575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, 575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
576 in_params.sample_rate); 576 in_params.sample_rate);
577 577
@@ -650,7 +650,7 @@ void CommandGenerator::GenerateFinalMixCommand() {
650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand"); 650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
651 } 651 }
652 auto& mix_info = mix_context.GetFinalMixInfo(); 652 auto& mix_info = mix_context.GetFinalMixInfo();
653 const auto in_params = mix_info.GetInParams(); 653 const auto& in_params = mix_info.GetInParams();
654 654
655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, 655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
656 in_params.sample_rate); 656 in_params.sample_rate);
@@ -674,7 +674,7 @@ void CommandGenerator::GenerateFinalMixCommand() {
674 674
675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
676 s32 sample_count, s32 channel, std::size_t mix_offset) { 676 s32 sample_count, s32 channel, std::size_t mix_offset) {
677 auto& in_params = voice_info.GetInParams(); 677 const auto& in_params = voice_info.GetInParams();
678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
679 if (wave_buffer.buffer_address == 0) { 679 if (wave_buffer.buffer_address == 0) {
680 return 0; 680 return 0;
@@ -714,7 +714,7 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
714 714
715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, 715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
716 s32 sample_count, s32 channel, std::size_t mix_offset) { 716 s32 sample_count, s32 channel, std::size_t mix_offset) {
717 auto& in_params = voice_info.GetInParams(); 717 const auto& in_params = voice_info.GetInParams();
718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; 718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
719 if (wave_buffer.buffer_address == 0) { 719 if (wave_buffer.buffer_address == 0) {
720 return 0; 720 return 0;
@@ -766,8 +766,8 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
766 val = std::clamp<s32>(val, -32768, 32767); 766 val = std::clamp<s32>(val, -32768, 32767);
767 // Advance output feedback. 767 // Advance output feedback.
768 yn2 = yn1; 768 yn2 = yn1;
769 yn1 = val; 769 yn1 = static_cast<s16>(val);
770 return static_cast<s16>(val); 770 return yn1;
771 }; 771 };
772 772
773 std::size_t buffer_offset{}; 773 std::size_t buffer_offset{};
@@ -853,7 +853,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
853 VoiceState& dsp_state, s32 channel, 853 VoiceState& dsp_state, s32 channel,
854 s32 target_sample_rate, s32 sample_count, 854 s32 target_sample_rate, s32 sample_count,
855 s32 node_id) { 855 s32 node_id) {
856 auto& in_params = voice_info.GetInParams(); 856 const auto& in_params = voice_info.GetInParams();
857 if (dumping_frame) { 857 if (dumping_frame) {
858 LOG_DEBUG(Audio, 858 LOG_DEBUG(Audio,
859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, " 859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
@@ -867,7 +867,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) * 867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f))); 868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
869 auto* output_base = output; 869 auto* output_base = output;
870 if ((dsp_state.fraction + sample_count * resample_rate) > (SCALED_MIX_BUFFER_SIZE - 4ULL)) { 870 if (dsp_state.fraction + sample_count * resample_rate >
871 static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
871 return; 872 return;
872 } 873 }
873 874
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 7356d252e..dc6f4af3a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -35,8 +35,8 @@ public:
35 std::optional<u8> option) override; 35 std::optional<u8> option) override;
36 36
37 ARM_Dynarmic_32& parent; 37 ARM_Dynarmic_32& parent;
38 u32 uprw; 38 u32 uprw = 0;
39 u32 uro; 39 u32 uro = 0;
40}; 40};
41 41
42} // namespace Core 42} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index df81e8e2c..81e8cc338 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -178,7 +178,7 @@ struct System::Impl {
178 arp_manager.ResetAll(); 178 arp_manager.ResetAll();
179 179
180 telemetry_session = std::make_unique<Core::TelemetrySession>(); 180 telemetry_session = std::make_unique<Core::TelemetrySession>();
181 service_manager = std::make_shared<Service::SM::ServiceManager>(); 181 service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
182 182
183 Service::Init(service_manager, system); 183 Service::Init(service_manager, system);
184 GDBStub::DeferStart(); 184 GDBStub::DeferStart();
@@ -221,7 +221,7 @@ struct System::Impl {
221 telemetry_session->AddInitialInfo(*app_loader); 221 telemetry_session->AddInitialInfo(*app_loader);
222 auto main_process = 222 auto main_process =
223 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); 223 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
224 const auto [load_result, load_parameters] = app_loader->Load(*main_process); 224 const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
225 if (load_result != Loader::ResultStatus::Success) { 225 if (load_result != Loader::ResultStatus::Success) {
226 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 226 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
227 Shutdown(); 227 Shutdown();
@@ -629,11 +629,11 @@ Loader::AppLoader& System::GetAppLoader() const {
629 return *impl->app_loader; 629 return *impl->app_loader;
630} 630}
631 631
632void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { 632void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
633 impl->virtual_filesystem = std::move(vfs); 633 impl->virtual_filesystem = std::move(vfs);
634} 634}
635 635
636std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { 636FileSys::VirtualFilesystem System::GetFilesystem() const {
637 return impl->virtual_filesystem; 637 return impl->virtual_filesystem;
638} 638}
639 639
diff --git a/src/core/core.h b/src/core/core.h
index 5c6cfbffe..83ded63a5 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -316,9 +316,9 @@ public:
316 Service::SM::ServiceManager& ServiceManager(); 316 Service::SM::ServiceManager& ServiceManager();
317 const Service::SM::ServiceManager& ServiceManager() const; 317 const Service::SM::ServiceManager& ServiceManager() const;
318 318
319 void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); 319 void SetFilesystem(FileSys::VirtualFilesystem vfs);
320 320
321 std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; 321 FileSys::VirtualFilesystem GetFilesystem() const;
322 322
323 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, 323 void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
324 const std::array<u8, 0x20>& build_id, VAddr main_region_begin, 324 const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dc591c730..65d246050 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -23,7 +23,6 @@
23#include "common/hex_util.h" 23#include "common/hex_util.h"
24#include "common/logging/log.h" 24#include "common/logging/log.h"
25#include "common/string_util.h" 25#include "common/string_util.h"
26#include "core/core.h"
27#include "core/crypto/aes_util.h" 26#include "core/crypto/aes_util.h"
28#include "core/crypto/key_manager.h" 27#include "core/crypto/key_manager.h"
29#include "core/crypto/partition_data_manager.h" 28#include "core/crypto/partition_data_manager.h"
@@ -1022,10 +1021,10 @@ void KeyManager::DeriveBase() {
1022 } 1021 }
1023} 1022}
1024 1023
1025void KeyManager::DeriveETicket(PartitionDataManager& data) { 1024void KeyManager::DeriveETicket(PartitionDataManager& data,
1025 const FileSys::ContentProvider& provider) {
1026 // ETicket keys 1026 // ETicket keys
1027 const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( 1027 const auto es = provider.GetEntry(0x0100000000000033, FileSys::ContentRecordType::Program);
1028 0x0100000000000033, FileSys::ContentRecordType::Program);
1029 1028
1030 if (es == nullptr) { 1029 if (es == nullptr) {
1031 return; 1030 return;
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 321b75323..0a7220286 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -20,6 +20,10 @@ namespace Common::FS {
20class IOFile; 20class IOFile;
21} 21}
22 22
23namespace FileSys {
24class ContentProvider;
25}
26
23namespace Loader { 27namespace Loader {
24enum class ResultStatus : u16; 28enum class ResultStatus : u16;
25} 29}
@@ -252,7 +256,7 @@ public:
252 256
253 bool BaseDeriveNecessary() const; 257 bool BaseDeriveNecessary() const;
254 void DeriveBase(); 258 void DeriveBase();
255 void DeriveETicket(PartitionDataManager& data); 259 void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider);
256 void PopulateTickets(); 260 void PopulateTickets();
257 void SynthesizeTickets(); 261 void SynthesizeTickets();
258 262
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index e04a54c3c..7c6304ff0 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -4,10 +4,10 @@
4 4
5#include <fmt/format.h> 5#include <fmt/format.h>
6#include "common/file_util.h" 6#include "common/file_util.h"
7#include "core/core.h"
8#include "core/file_sys/bis_factory.h" 7#include "core/file_sys/bis_factory.h"
9#include "core/file_sys/mode.h" 8#include "core/file_sys/mode.h"
10#include "core/file_sys/registered_cache.h" 9#include "core/file_sys/registered_cache.h"
10#include "core/file_sys/vfs.h"
11 11
12namespace FileSys { 12namespace FileSys {
13 13
@@ -81,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
81 } 81 }
82} 82}
83 83
84VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { 84VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
85 VirtualFilesystem file_system) const {
85 auto& keys = Core::Crypto::KeyManager::Instance(); 86 auto& keys = Core::Crypto::KeyManager::Instance();
86 Core::Crypto::PartitionDataManager pdm{ 87 Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
87 Core::System::GetInstance().GetFilesystem()->OpenDirectory( 88 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
88 Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
89 keys.PopulateFromPartitionData(pdm); 89 keys.PopulateFromPartitionData(pdm);
90 90
91 switch (id) { 91 switch (id) {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 438d3f8d8..136485881 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -52,7 +52,7 @@ public:
52 VirtualDir GetModificationDumpRoot(u64 title_id) const; 52 VirtualDir GetModificationDumpRoot(u64 title_id) const;
53 53
54 VirtualDir OpenPartition(BisPartitionId id) const; 54 VirtualDir OpenPartition(BisPartitionId id) const;
55 VirtualFile OpenPartitionStorage(BisPartitionId id) const; 55 VirtualFile OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const;
56 56
57 VirtualDir GetImageDirectory() const; 57 VirtualDir GetImageDirectory() const;
58 58
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 9ab86e35b..403c4219a 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -83,7 +83,7 @@ enum class Language : u8 {
83 Italian = 7, 83 Italian = 7,
84 Dutch = 8, 84 Dutch = 8,
85 CanadianFrench = 9, 85 CanadianFrench = 9,
86 Portugese = 10, 86 Portuguese = 10,
87 Russian = 11, 87 Russian = 11,
88 Korean = 12, 88 Korean = 12,
89 Taiwanese = 13, 89 Taiwanese = 13,
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index fe7375e84..5990a2fd5 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -12,6 +12,49 @@
12#include "core/file_sys/nca_patch.h" 12#include "core/file_sys/nca_patch.h"
13 13
14namespace FileSys { 14namespace FileSys {
15namespace {
16template <bool Subsection, typename BlockType, typename BucketType>
17std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockType& block,
18 const BucketType& buckets) {
19 if constexpr (Subsection) {
20 const auto& last_bucket = buckets[block.number_buckets - 1];
21 if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) {
22 return {block.number_buckets - 1, last_bucket.number_entries};
23 }
24 } else {
25 ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
26 }
27
28 std::size_t bucket_id = std::count_if(
29 block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
30 [&offset](u64 base_offset) { return base_offset <= offset; });
31
32 const auto& bucket = buckets[bucket_id];
33
34 if (bucket.number_entries == 1) {
35 return {bucket_id, 0};
36 }
37
38 std::size_t low = 0;
39 std::size_t mid = 0;
40 std::size_t high = bucket.number_entries - 1;
41 while (low <= high) {
42 mid = (low + high) / 2;
43 if (bucket.entries[mid].address_patch > offset) {
44 high = mid - 1;
45 } else {
46 if (mid == bucket.number_entries - 1 ||
47 bucket.entries[mid + 1].address_patch > offset) {
48 return {bucket_id, mid};
49 }
50
51 low = mid + 1;
52 }
53 }
54
55 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
56}
57} // Anonymous namespace
15 58
16BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, 59BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
17 std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_, 60 std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
@@ -110,46 +153,6 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
110 return raw_read; 153 return raw_read;
111} 154}
112 155
113template <bool Subsection, typename BlockType, typename BucketType>
114std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
115 BucketType buckets) const {
116 if constexpr (Subsection) {
117 const auto last_bucket = buckets[block.number_buckets - 1];
118 if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
119 return {block.number_buckets - 1, last_bucket.number_entries};
120 } else {
121 ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
122 }
123
124 std::size_t bucket_id = std::count_if(
125 block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
126 [&offset](u64 base_offset) { return base_offset <= offset; });
127
128 const auto bucket = buckets[bucket_id];
129
130 if (bucket.number_entries == 1)
131 return {bucket_id, 0};
132
133 std::size_t low = 0;
134 std::size_t mid = 0;
135 std::size_t high = bucket.number_entries - 1;
136 while (low <= high) {
137 mid = (low + high) / 2;
138 if (bucket.entries[mid].address_patch > offset) {
139 high = mid - 1;
140 } else {
141 if (mid == bucket.number_entries - 1 ||
142 bucket.entries[mid + 1].address_patch > offset) {
143 return {bucket_id, mid};
144 }
145
146 low = mid + 1;
147 }
148 }
149
150 UNREACHABLE_MSG("Offset could not be found in BKTR block.");
151}
152
153RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { 156RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
154 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); 157 const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
155 return relocation_buckets[res.first].entries[res.second]; 158 return relocation_buckets[res.first].entries[res.second];
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
index 8e64e8378..60c544f8e 100644
--- a/src/core/file_sys/nca_patch.h
+++ b/src/core/file_sys/nca_patch.h
@@ -117,10 +117,6 @@ public:
117 bool Rename(std::string_view name) override; 117 bool Rename(std::string_view name) override;
118 118
119private: 119private:
120 template <bool Subsection, typename BlockType, typename BucketType>
121 std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
122 BucketType buckets) const;
123
124 RelocationEntry GetRelocationEntry(u64 offset) const; 120 RelocationEntry GetRelocationEntry(u64 offset) const;
125 RelocationEntry GetNextRelocationEntry(u64 offset) const; 121 RelocationEntry GetNextRelocationEntry(u64 offset) const;
126 122
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index c228d253e..b9c09b456 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -27,6 +27,7 @@
27#include "core/settings.h" 27#include "core/settings.h"
28 28
29namespace FileSys { 29namespace FileSys {
30namespace {
30 31
31constexpr u64 SINGLE_BYTE_MODULUS = 0x100; 32constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
32constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 33constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
@@ -36,19 +37,28 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
36 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 37 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
37}; 38};
38 39
39std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 40enum class TitleVersionFormat : u8 {
41 ThreeElements, ///< vX.Y.Z
42 FourElements, ///< vX.Y.Z.W
43};
44
45std::string FormatTitleVersion(u32 version,
46 TitleVersionFormat format = TitleVersionFormat::ThreeElements) {
40 std::array<u8, sizeof(u32)> bytes{}; 47 std::array<u8, sizeof(u32)> bytes{};
41 bytes[0] = version % SINGLE_BYTE_MODULUS; 48 bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
42 for (std::size_t i = 1; i < bytes.size(); ++i) { 49 for (std::size_t i = 1; i < bytes.size(); ++i) {
43 version /= SINGLE_BYTE_MODULUS; 50 version /= SINGLE_BYTE_MODULUS;
44 bytes[i] = version % SINGLE_BYTE_MODULUS; 51 bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
45 } 52 }
46 53
47 if (format == TitleVersionFormat::FourElements) 54 if (format == TitleVersionFormat::FourElements) {
48 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); 55 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
56 }
49 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); 57 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
50} 58}
51 59
60// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
61// doesn't have a directory with name.
52VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { 62VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
53#ifdef _WIN32 63#ifdef _WIN32
54 return dir->GetSubdirectory(name); 64 return dir->GetSubdirectory(name);
@@ -65,6 +75,43 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
65#endif 75#endif
66} 76}
67 77
78std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
79 u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
80 const auto build_id_raw = Common::HexToString(build_id_, upper);
81 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
82 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
83
84 if (file == nullptr) {
85 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
86 title_id, build_id);
87 return std::nullopt;
88 }
89
90 std::vector<u8> data(file->GetSize());
91 if (file->Read(data.data(), data.size()) != data.size()) {
92 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
93 title_id, build_id);
94 return std::nullopt;
95 }
96
97 const Core::Memory::TextCheatParser parser;
98 return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
99}
100
101void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
102 if (to.empty()) {
103 to += with;
104 } else {
105 to += ", ";
106 to += with;
107 }
108}
109
110bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
111 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
112}
113} // Anonymous namespace
114
68PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 115PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
69 116
70PatchManager::~PatchManager() = default; 117PatchManager::~PatchManager() = default;
@@ -245,7 +292,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
245 return out; 292 return out;
246} 293}
247 294
248bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { 295bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
249 const auto build_id_raw = Common::HexToString(build_id_); 296 const auto build_id_raw = Common::HexToString(build_id_);
250 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 297 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
251 298
@@ -265,36 +312,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
265 return !CollectPatches(patch_dirs, build_id).empty(); 312 return !CollectPatches(patch_dirs, build_id).empty();
266} 313}
267 314
268namespace {
269std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
270 const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
271 const VirtualDir& base_path, bool upper) {
272 const auto build_id_raw = Common::HexToString(build_id_, upper);
273 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
274 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
275
276 if (file == nullptr) {
277 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
278 title_id, build_id);
279 return std::nullopt;
280 }
281
282 std::vector<u8> data(file->GetSize());
283 if (file->Read(data.data(), data.size()) != data.size()) {
284 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
285 title_id, build_id);
286 return std::nullopt;
287 }
288
289 Core::Memory::TextCheatParser parser;
290 return parser.Parse(system,
291 std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
292}
293
294} // Anonymous namespace
295
296std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 315std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
297 const Core::System& system, const std::array<u8, 32>& build_id_) const { 316 const Core::System& system, const BuildID& build_id_) const {
298 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); 317 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
299 if (load_dir == nullptr) { 318 if (load_dir == nullptr) {
300 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 319 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -314,14 +333,12 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
314 333
315 auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); 334 auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
316 if (cheats_dir != nullptr) { 335 if (cheats_dir != nullptr) {
317 auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); 336 if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) {
318 if (res.has_value()) {
319 std::copy(res->begin(), res->end(), std::back_inserter(out)); 337 std::copy(res->begin(), res->end(), std::back_inserter(out));
320 continue; 338 continue;
321 } 339 }
322 340
323 res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); 341 if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) {
324 if (res.has_value()) {
325 std::copy(res->begin(), res->end(), std::back_inserter(out)); 342 std::copy(res->begin(), res->end(), std::back_inserter(out));
326 } 343 }
327 } 344 }
@@ -435,21 +452,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
435 return romfs; 452 return romfs;
436} 453}
437 454
438static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { 455PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
439 if (to.empty()) 456 if (title_id == 0) {
440 to += with;
441 else
442 to += ", " + with;
443}
444
445static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
446 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
447}
448
449std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
450 VirtualFile update_raw) const {
451 if (title_id == 0)
452 return {}; 457 return {};
458 }
459
453 std::map<std::string, std::string, std::less<>> out; 460 std::map<std::string, std::string, std::less<>> out;
454 const auto& installed = Core::System::GetInstance().GetContentProvider(); 461 const auto& installed = Core::System::GetInstance().GetContentProvider();
455 const auto& disabled = Settings::values.disabled_addons[title_id]; 462 const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -472,8 +479,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
472 if (meta_ver.value_or(0) == 0) { 479 if (meta_ver.value_or(0) == 0) {
473 out.insert_or_assign(update_label, ""); 480 out.insert_or_assign(update_label, "");
474 } else { 481 } else {
475 out.insert_or_assign( 482 out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
476 update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
477 } 483 }
478 } else if (update_raw != nullptr) { 484 } else if (update_raw != nullptr) {
479 out.insert_or_assign(update_label, "PACKED"); 485 out.insert_or_assign(update_label, "PACKED");
@@ -562,40 +568,46 @@ std::optional<u32> PatchManager::GetGameVersion() const {
562 return installed.GetEntryVersion(title_id); 568 return installed.GetEntryVersion(title_id);
563} 569}
564 570
565std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 571PatchManager::Metadata PatchManager::GetControlMetadata() const {
566 const auto& installed = Core::System::GetInstance().GetContentProvider(); 572 const auto& installed = Core::System::GetInstance().GetContentProvider();
567 573
568 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); 574 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
569 if (base_control_nca == nullptr) 575 if (base_control_nca == nullptr) {
570 return {}; 576 return {};
577 }
571 578
572 return ParseControlNCA(*base_control_nca); 579 return ParseControlNCA(*base_control_nca);
573} 580}
574 581
575std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { 582PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
576 const auto base_romfs = nca.GetRomFS(); 583 const auto base_romfs = nca.GetRomFS();
577 if (base_romfs == nullptr) 584 if (base_romfs == nullptr) {
578 return {}; 585 return {};
586 }
579 587
580 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); 588 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
581 if (romfs == nullptr) 589 if (romfs == nullptr) {
582 return {}; 590 return {};
591 }
583 592
584 const auto extracted = ExtractRomFS(romfs); 593 const auto extracted = ExtractRomFS(romfs);
585 if (extracted == nullptr) 594 if (extracted == nullptr) {
586 return {}; 595 return {};
596 }
587 597
588 auto nacp_file = extracted->GetFile("control.nacp"); 598 auto nacp_file = extracted->GetFile("control.nacp");
589 if (nacp_file == nullptr) 599 if (nacp_file == nullptr) {
590 nacp_file = extracted->GetFile("Control.nacp"); 600 nacp_file = extracted->GetFile("Control.nacp");
601 }
591 602
592 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); 603 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
593 604
594 VirtualFile icon_file; 605 VirtualFile icon_file;
595 for (const auto& language : FileSys::LANGUAGE_NAMES) { 606 for (const auto& language : FileSys::LANGUAGE_NAMES) {
596 icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); 607 icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat"));
597 if (icon_file != nullptr) 608 if (icon_file != nullptr) {
598 break; 609 break;
610 }
599 } 611 }
600 612
601 return {std::move(nacp), icon_file}; 613 return {std::move(nacp), icon_file};
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 532f4995f..1f28c6241 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -22,70 +22,62 @@ namespace FileSys {
22class NCA; 22class NCA;
23class NACP; 23class NACP;
24 24
25enum class TitleVersionFormat : u8 {
26 ThreeElements, ///< vX.Y.Z
27 FourElements, ///< vX.Y.Z.W
28};
29
30std::string FormatTitleVersion(u32 version,
31 TitleVersionFormat format = TitleVersionFormat::ThreeElements);
32
33// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
34// doesn't have a directory with name.
35VirtualDir FindSubdirectoryCaseless(VirtualDir dir, std::string_view name);
36
37// A centralized class to manage patches to games. 25// A centralized class to manage patches to games.
38class PatchManager { 26class PatchManager {
39public: 27public:
28 using BuildID = std::array<u8, 0x20>;
29 using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
30 using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
31
40 explicit PatchManager(u64 title_id); 32 explicit PatchManager(u64 title_id);
41 ~PatchManager(); 33 ~PatchManager();
42 34
43 u64 GetTitleID() const; 35 [[nodiscard]] u64 GetTitleID() const;
44 36
45 // Currently tracked ExeFS patches: 37 // Currently tracked ExeFS patches:
46 // - Game Updates 38 // - Game Updates
47 VirtualDir PatchExeFS(VirtualDir exefs) const; 39 [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const;
48 40
49 // Currently tracked NSO patches: 41 // Currently tracked NSO patches:
50 // - IPS 42 // - IPS
51 // - IPSwitch 43 // - IPSwitch
52 std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; 44 [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso,
45 const std::string& name) const;
53 46
54 // Checks to see if PatchNSO() will have any effect given the NSO's build ID. 47 // Checks to see if PatchNSO() will have any effect given the NSO's build ID.
55 // Used to prevent expensive copies in NSO loader. 48 // Used to prevent expensive copies in NSO loader.
56 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 49 [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const;
57 50
58 // Creates a CheatList object with all 51 // Creates a CheatList object with all
59 std::vector<Core::Memory::CheatEntry> CreateCheatList( 52 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
60 const Core::System& system, const std::array<u8, 0x20>& build_id) const; 53 const Core::System& system, const BuildID& build_id) const;
61 54
62 // Currently tracked RomFS patches: 55 // Currently tracked RomFS patches:
63 // - Game Updates 56 // - Game Updates
64 // - LayeredFS 57 // - LayeredFS
65 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 58 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
66 ContentRecordType type = ContentRecordType::Program, 59 ContentRecordType type = ContentRecordType::Program,
67 VirtualFile update_raw = nullptr) const; 60 VirtualFile update_raw = nullptr) const;
68 61
69 // Returns a vector of pairs between patch names and patch versions. 62 // Returns a vector of pairs between patch names and patch versions.
70 // i.e. Update 3.2.2 will return {"Update", "3.2.2"} 63 // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
71 std::map<std::string, std::string, std::less<>> GetPatchVersionNames( 64 [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
72 VirtualFile update_raw = nullptr) const;
73 65
74 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, 66 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
75 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be 67 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
76 // std::nullopt 68 // std::nullopt
77 std::optional<u32> GetGameVersion() const; 69 [[nodiscard]] std::optional<u32> GetGameVersion() const;
78 70
79 // Given title_id of the program, attempts to get the control data of the update and parse 71 // Given title_id of the program, attempts to get the control data of the update and parse
80 // it, falling back to the base control data. 72 // it, falling back to the base control data.
81 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; 73 [[nodiscard]] Metadata GetControlMetadata() const;
82 74
83 // Version of GetControlMetadata that takes an arbitrary NCA 75 // Version of GetControlMetadata that takes an arbitrary NCA
84 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; 76 [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
85 77
86private: 78private:
87 std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, 79 [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
88 const std::string& build_id) const; 80 const std::string& build_id) const;
89 81
90 u64 title_id; 82 u64 title_id;
91}; 83};
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 418a39a7e..e967a254e 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -6,7 +6,6 @@
6#include "common/assert.h" 6#include "common/assert.h"
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
11#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
12#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
@@ -19,7 +18,9 @@
19 18
20namespace FileSys { 19namespace FileSys {
21 20
22RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { 21RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
22 Service::FileSystem::FileSystemController& controller)
23 : content_provider{provider}, filesystem_controller{controller} {
23 // Load the RomFS from the app 24 // Load the RomFS from the app
24 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { 25 if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
25 LOG_ERROR(Service_FS, "Unable to read RomFS!"); 26 LOG_ERROR(Service_FS, "Unable to read RomFS!");
@@ -46,39 +47,38 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_titl
46 47
47ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, 48ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
48 ContentRecordType type) const { 49 ContentRecordType type) const {
49 std::shared_ptr<NCA> res; 50 const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
50
51 switch (storage) {
52 case StorageId::None:
53 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
54 break;
55 case StorageId::NandSystem:
56 res =
57 Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
58 title_id, type);
59 break;
60 case StorageId::NandUser:
61 res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
62 title_id, type);
63 break;
64 case StorageId::SdCard:
65 res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
66 title_id, type);
67 break;
68 default:
69 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
70 }
71
72 if (res == nullptr) { 51 if (res == nullptr) {
73 // TODO(DarkLordZach): Find the right error code to use here 52 // TODO(DarkLordZach): Find the right error code to use here
74 return RESULT_UNKNOWN; 53 return RESULT_UNKNOWN;
75 } 54 }
55
76 const auto romfs = res->GetRomFS(); 56 const auto romfs = res->GetRomFS();
77 if (romfs == nullptr) { 57 if (romfs == nullptr) {
78 // TODO(DarkLordZach): Find the right error code to use here 58 // TODO(DarkLordZach): Find the right error code to use here
79 return RESULT_UNKNOWN; 59 return RESULT_UNKNOWN;
80 } 60 }
61
81 return MakeResult<VirtualFile>(romfs); 62 return MakeResult<VirtualFile>(romfs);
82} 63}
83 64
65std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage,
66 ContentRecordType type) const {
67 switch (storage) {
68 case StorageId::None:
69 return content_provider.GetEntry(title_id, type);
70 case StorageId::NandSystem:
71 return filesystem_controller.GetSystemNANDContents()->GetEntry(title_id, type);
72 case StorageId::NandUser:
73 return filesystem_controller.GetUserNANDContents()->GetEntry(title_id, type);
74 case StorageId::SdCard:
75 return filesystem_controller.GetSDMCContents()->GetEntry(title_id, type);
76 case StorageId::Host:
77 case StorageId::GameCard:
78 default:
79 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
80 return nullptr;
81 }
82}
83
84} // namespace FileSys 84} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index c5d40285c..ec704dfa8 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -13,8 +13,15 @@ namespace Loader {
13class AppLoader; 13class AppLoader;
14} // namespace Loader 14} // namespace Loader
15 15
16namespace Service::FileSystem {
17class FileSystemController;
18}
19
16namespace FileSys { 20namespace FileSys {
17 21
22class ContentProvider;
23class NCA;
24
18enum class ContentRecordType : u8; 25enum class ContentRecordType : u8;
19 26
20enum class StorageId : u8 { 27enum class StorageId : u8 {
@@ -29,18 +36,26 @@ enum class StorageId : u8 {
29/// File system interface to the RomFS archive 36/// File system interface to the RomFS archive
30class RomFSFactory { 37class RomFSFactory {
31public: 38public:
32 explicit RomFSFactory(Loader::AppLoader& app_loader); 39 explicit RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
40 Service::FileSystem::FileSystemController& controller);
33 ~RomFSFactory(); 41 ~RomFSFactory();
34 42
35 void SetPackedUpdate(VirtualFile update_raw); 43 void SetPackedUpdate(VirtualFile update_raw);
36 ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; 44 [[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; 45 [[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage,
46 ContentRecordType type) const;
38 47
39private: 48private:
49 [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage,
50 ContentRecordType type) const;
51
40 VirtualFile file; 52 VirtualFile file;
41 VirtualFile update_raw; 53 VirtualFile update_raw;
42 bool updatable; 54 bool updatable;
43 u64 ivfc_offset; 55 u64 ivfc_offset;
56
57 ContentProvider& content_provider;
58 Service::FileSystem::FileSystemController& filesystem_controller;
44}; 59};
45 60
46} // namespace FileSys 61} // namespace FileSys
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 2b098b7c6..9da0d2829 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -119,11 +119,11 @@ using ButtonDevice = InputDevice<bool>;
119using AnalogDevice = InputDevice<std::tuple<float, float>>; 119using AnalogDevice = InputDevice<std::tuple<float, float>>;
120 120
121/** 121/**
122 * A motion device is an input device that returns a tuple of accelerometer state vector and 122 * A motion status is an object that returns a tuple of accelerometer state vector,
123 * gyroscope state vector. 123 * gyroscope state vector, rotation state vector and orientation state matrix.
124 * 124 *
125 * For both vectors: 125 * For both vectors:
126 * x+ is the same direction as LEFT on D-pad. 126 * x+ is the same direction as RIGHT on D-pad.
127 * y+ is normal to the touch screen, pointing outward. 127 * y+ is normal to the touch screen, pointing outward.
128 * z+ is the same direction as UP on D-pad. 128 * z+ is the same direction as UP on D-pad.
129 * 129 *
@@ -133,8 +133,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
133 * For gyroscope state vector: 133 * For gyroscope state vector:
134 * Orientation is determined by right-hand rule. 134 * Orientation is determined by right-hand rule.
135 * Units: deg/sec 135 * Units: deg/sec
136 *
137 * For rotation state vector
138 * Units: rotations
139 *
140 * For orientation state matrix
141 * x vector
142 * y vector
143 * z vector
144 */
145using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
146 std::array<Common::Vec3f, 3>>;
147
148/**
149 * A motion device is an input device that returns a motion status object
136 */ 150 */
137using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; 151using MotionDevice = InputDevice<MotionStatus>;
138 152
139/** 153/**
140 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are 154 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 5ab204b9b..be9eba519 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -48,14 +48,15 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
48} 48}
49 49
50ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, 50ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
51 Core::Memory::Memory& memory) { 51 Core::Memory::Memory& memory,
52 Core::Timing::CoreTiming& core_timing) {
52 // Keep ServerSession alive until we're done working with it. 53 // Keep ServerSession alive until we're done working with it.
53 if (!parent->Server()) { 54 if (!parent->Server()) {
54 return ERR_SESSION_CLOSED_BY_REMOTE; 55 return ERR_SESSION_CLOSED_BY_REMOTE;
55 } 56 }
56 57
57 // Signal the server session that new data is available 58 // Signal the server session that new data is available
58 return parent->Server()->HandleSyncRequest(std::move(thread), memory); 59 return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
59} 60}
60 61
61} // namespace Kernel 62} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index c5f760d7d..e5e0690c2 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -16,6 +16,10 @@ namespace Core::Memory {
16class Memory; 16class Memory;
17} 17}
18 18
19namespace Core::Timing {
20class CoreTiming;
21}
22
19namespace Kernel { 23namespace Kernel {
20 24
21class KernelCore; 25class KernelCore;
@@ -42,7 +46,8 @@ public:
42 return HANDLE_TYPE; 46 return HANDLE_TYPE;
43 } 47 }
44 48
45 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 49 ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
50 Core::Timing::CoreTiming& core_timing);
46 51
47 bool ShouldWait(const Thread* thread) const override; 52 bool ShouldWait(const Thread* thread) const override;
48 53
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 7e6391c6c..8c19f2534 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -8,7 +8,6 @@
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/core_timing.h" 11#include "core/core_timing.h"
13#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
@@ -185,10 +184,11 @@ ResultCode ServerSession::CompleteSyncRequest() {
185} 184}
186 185
187ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, 186ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
188 Core::Memory::Memory& memory) { 187 Core::Memory::Memory& memory,
188 Core::Timing::CoreTiming& core_timing) {
189 const ResultCode result = QueueSyncRequest(std::move(thread), memory); 189 const ResultCode result = QueueSyncRequest(std::move(thread), memory);
190 const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; 190 const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
191 Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); 191 core_timing.ScheduleEvent(delay, request_event, {});
192 return result; 192 return result;
193} 193}
194 194
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 403aaf10b..d23e9ec68 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -18,8 +18,9 @@ class Memory;
18} 18}
19 19
20namespace Core::Timing { 20namespace Core::Timing {
21class CoreTiming;
21struct EventType; 22struct EventType;
22} 23} // namespace Core::Timing
23 24
24namespace Kernel { 25namespace Kernel {
25 26
@@ -87,12 +88,14 @@ public:
87 /** 88 /**
88 * Handle a sync request from the emulated application. 89 * Handle a sync request from the emulated application.
89 * 90 *
90 * @param thread Thread that initiated the request. 91 * @param thread Thread that initiated the request.
91 * @param memory Memory context to handle the sync request under. 92 * @param memory Memory context to handle the sync request under.
93 * @param core_timing Core timing context to schedule the request event under.
92 * 94 *
93 * @returns ResultCode from the operation. 95 * @returns ResultCode from the operation.
94 */ 96 */
95 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 97 ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
98 Core::Timing::CoreTiming& core_timing);
96 99
97 bool ShouldWait(const Thread* thread) const override; 100 bool ShouldWait(const Thread* thread) const override;
98 101
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 01ae57053..bafd1ced7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -346,7 +346,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
346 SchedulerLock lock(system.Kernel()); 346 SchedulerLock lock(system.Kernel());
347 thread->InvalidateHLECallback(); 347 thread->InvalidateHLECallback();
348 thread->SetStatus(ThreadStatus::WaitIPC); 348 thread->SetStatus(ThreadStatus::WaitIPC);
349 session->SendSyncRequest(SharedFrom(thread), system.Memory()); 349 session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
350 } 350 }
351 351
352 if (thread->HasHLECallback()) { 352 if (thread->HasHLECallback()) {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7d92b25a3..d7a81f64a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1192,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1192 {120, nullptr, "ExecuteProgram"}, 1192 {120, nullptr, "ExecuteProgram"},
1193 {121, nullptr, "ClearUserChannel"}, 1193 {121, nullptr, "ClearUserChannel"},
1194 {122, nullptr, "UnpopToUserChannel"}, 1194 {122, nullptr, "UnpopToUserChannel"},
1195 {123, nullptr, "GetPreviousProgramIndex"}, 1195 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1196 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1197 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
1198 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, 1198 {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
@@ -1554,6 +1554,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1554 rb.Push<u32>(0); 1554 rb.Push<u32>(0);
1555} 1555}
1556 1556
1557void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1558 LOG_WARNING(Service_AM, "(STUBBED) called");
1559
1560 IPC::ResponseBuilder rb{ctx, 3};
1561 rb.Push(RESULT_SUCCESS);
1562 rb.Push<s32>(previous_program_index);
1563}
1564
1557void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { 1565void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
1558 LOG_WARNING(Service_AM, "(STUBBED) called"); 1566 LOG_WARNING(Service_AM, "(STUBBED) called");
1559 1567
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6e69796ec..bcc06affe 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -288,11 +288,13 @@ private:
288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 288 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 290 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
291 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
291 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 292 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
292 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 293 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
293 294
294 bool launch_popped_application_specific = false; 295 bool launch_popped_application_specific = false;
295 bool launch_popped_account_preselect = false; 296 bool launch_popped_account_preselect = false;
297 s32 previous_program_index{-1};
296 Kernel::EventPair gpu_error_detected_event; 298 Kernel::EventPair gpu_error_detected_event;
297 Kernel::EventPair friend_invitation_storage_channel_event; 299 Kernel::EventPair friend_invitation_storage_channel_event;
298 Core::System& system; 300 Core::System& system;
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 2cee1193c..54a5fb84b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -379,7 +379,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
379 return FileSys::ERROR_ENTITY_NOT_FOUND; 379 return FileSys::ERROR_ENTITY_NOT_FOUND;
380 } 380 }
381 381
382 auto part = bis_factory->OpenPartitionStorage(id); 382 auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
383 if (part == nullptr) { 383 if (part == nullptr) {
384 return FileSys::ERROR_INVALID_ARGUMENT; 384 return FileSys::ERROR_INVALID_ARGUMENT;
385 } 385 }
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 59f528dfb..620386cd1 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -250,6 +250,9 @@ void Controller_NPad::OnLoadInputDevices() {
250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, 250 std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, 251 players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); 252 sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
253 std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
254 players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
255 motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
253 } 256 }
254} 257}
255 258
@@ -266,6 +269,7 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
266 auto& rstick_entry = npad_pad_states[controller_idx].r_stick; 269 auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
267 const auto& button_state = buttons[controller_idx]; 270 const auto& button_state = buttons[controller_idx];
268 const auto& analog_state = sticks[controller_idx]; 271 const auto& analog_state = sticks[controller_idx];
272 const auto& motion_state = motions[controller_idx];
269 const auto [stick_l_x_f, stick_l_y_f] = 273 const auto [stick_l_x_f, stick_l_y_f] =
270 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); 274 analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
271 const auto [stick_r_x_f, stick_r_y_f] = 275 const auto [stick_r_x_f, stick_r_y_f] =
@@ -360,6 +364,45 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
360 continue; 364 continue;
361 } 365 }
362 const u32 npad_index = static_cast<u32>(i); 366 const u32 npad_index = static_cast<u32>(i);
367
368 const std::array<SixAxisGeneric*, 6> controller_sixaxes{
369 &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
370 &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
371 };
372
373 for (auto* sixaxis_sensor : controller_sixaxes) {
374 sixaxis_sensor->common.entry_count = 16;
375 sixaxis_sensor->common.total_entry_count = 17;
376
377 const auto& last_entry =
378 sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
379
380 sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
381 sixaxis_sensor->common.last_entry_index =
382 (sixaxis_sensor->common.last_entry_index + 1) % 17;
383
384 auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
385
386 cur_entry.timestamp = last_entry.timestamp + 1;
387 cur_entry.timestamp2 = cur_entry.timestamp;
388 }
389
390 // Try to read sixaxis sensor states
391 std::array<MotionDevice, 2> motion_devices;
392
393 if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
394 sixaxis_at_rest = true;
395 for (std::size_t e = 0; e < motion_devices.size(); ++e) {
396 const auto& device = motions[i][e];
397 if (device) {
398 std::tie(motion_devices[e].accel, motion_devices[e].gyro,
399 motion_devices[e].rotation, motion_devices[e].orientation) =
400 device->GetStatus();
401 sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
402 }
403 }
404 }
405
363 RequestPadStateUpdate(npad_index); 406 RequestPadStateUpdate(npad_index);
364 auto& pad_state = npad_pad_states[npad_index]; 407 auto& pad_state = npad_pad_states[npad_index];
365 408
@@ -377,6 +420,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
377 420
378 libnx_entry.connection_status.raw = 0; 421 libnx_entry.connection_status.raw = 0;
379 libnx_entry.connection_status.IsConnected.Assign(1); 422 libnx_entry.connection_status.IsConnected.Assign(1);
423 auto& full_sixaxis_entry =
424 npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
425 auto& handheld_sixaxis_entry =
426 npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
427 auto& dual_left_sixaxis_entry =
428 npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
429 auto& dual_right_sixaxis_entry =
430 npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
431 auto& left_sixaxis_entry =
432 npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
433 auto& right_sixaxis_entry =
434 npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
380 435
381 switch (controller_type) { 436 switch (controller_type) {
382 case NPadControllerType::None: 437 case NPadControllerType::None:
@@ -391,6 +446,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
391 main_controller.pad.r_stick = pad_state.r_stick; 446 main_controller.pad.r_stick = pad_state.r_stick;
392 447
393 libnx_entry.connection_status.IsWired.Assign(1); 448 libnx_entry.connection_status.IsWired.Assign(1);
449
450 if (sixaxis_sensors_enabled && motions[i][0]) {
451 full_sixaxis_entry.accel = motion_devices[0].accel;
452 full_sixaxis_entry.gyro = motion_devices[0].gyro;
453 full_sixaxis_entry.rotation = motion_devices[0].rotation;
454 full_sixaxis_entry.orientation = motion_devices[0].orientation;
455 }
394 break; 456 break;
395 case NPadControllerType::Handheld: 457 case NPadControllerType::Handheld:
396 handheld_entry.connection_status.raw = 0; 458 handheld_entry.connection_status.raw = 0;
@@ -409,6 +471,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
409 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 471 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
410 libnx_entry.connection_status.IsLeftJoyWired.Assign(1); 472 libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
411 libnx_entry.connection_status.IsRightJoyWired.Assign(1); 473 libnx_entry.connection_status.IsRightJoyWired.Assign(1);
474
475 if (sixaxis_sensors_enabled && motions[i][0]) {
476 handheld_sixaxis_entry.accel = motion_devices[0].accel;
477 handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
478 handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
479 handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
480 }
412 break; 481 break;
413 case NPadControllerType::JoyDual: 482 case NPadControllerType::JoyDual:
414 dual_entry.connection_status.raw = 0; 483 dual_entry.connection_status.raw = 0;
@@ -421,6 +490,21 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
421 490
422 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); 491 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
423 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 492 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
493
494 if (sixaxis_sensors_enabled && motions[i][0]) {
495 // Set motion for the left joycon
496 dual_left_sixaxis_entry.accel = motion_devices[0].accel;
497 dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
498 dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
499 dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
500 }
501 if (sixaxis_sensors_enabled && motions[i][1]) {
502 // Set motion for the right joycon
503 dual_right_sixaxis_entry.accel = motion_devices[1].accel;
504 dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
505 dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
506 dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
507 }
424 break; 508 break;
425 case NPadControllerType::JoyLeft: 509 case NPadControllerType::JoyLeft:
426 left_entry.connection_status.raw = 0; 510 left_entry.connection_status.raw = 0;
@@ -431,6 +515,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
431 left_entry.pad.r_stick = pad_state.r_stick; 515 left_entry.pad.r_stick = pad_state.r_stick;
432 516
433 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); 517 libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
518
519 if (sixaxis_sensors_enabled && motions[i][0]) {
520 left_sixaxis_entry.accel = motion_devices[0].accel;
521 left_sixaxis_entry.gyro = motion_devices[0].gyro;
522 left_sixaxis_entry.rotation = motion_devices[0].rotation;
523 left_sixaxis_entry.orientation = motion_devices[0].orientation;
524 }
434 break; 525 break;
435 case NPadControllerType::JoyRight: 526 case NPadControllerType::JoyRight:
436 right_entry.connection_status.raw = 0; 527 right_entry.connection_status.raw = 0;
@@ -441,6 +532,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
441 right_entry.pad.r_stick = pad_state.r_stick; 532 right_entry.pad.r_stick = pad_state.r_stick;
442 533
443 libnx_entry.connection_status.IsRightJoyConnected.Assign(1); 534 libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
535
536 if (sixaxis_sensors_enabled && motions[i][1]) {
537 right_sixaxis_entry.accel = motion_devices[1].accel;
538 right_sixaxis_entry.gyro = motion_devices[1].gyro;
539 right_sixaxis_entry.rotation = motion_devices[1].rotation;
540 right_sixaxis_entry.orientation = motion_devices[1].orientation;
541 }
444 break; 542 break;
445 case NPadControllerType::Pokeball: 543 case NPadControllerType::Pokeball:
446 pokeball_entry.connection_status.raw = 0; 544 pokeball_entry.connection_status.raw = 0;
@@ -590,6 +688,14 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
590 return gyroscope_zero_drift_mode; 688 return gyroscope_zero_drift_mode;
591} 689}
592 690
691bool Controller_NPad::IsSixAxisSensorAtRest() const {
692 return sixaxis_at_rest;
693}
694
695void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
696 sixaxis_sensors_enabled = six_axis_status;
697}
698
593void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { 699void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
594 const auto npad_index_1 = NPadIdToIndex(npad_id_1); 700 const auto npad_index_1 = NPadIdToIndex(npad_id_1);
595 const auto npad_index_2 = NPadIdToIndex(npad_id_2); 701 const auto npad_index_2 = NPadIdToIndex(npad_id_2);
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 54e950e1b..654d97c3f 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -139,6 +139,8 @@ public:
139 139
140 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); 140 void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
141 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; 141 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
142 bool IsSixAxisSensorAtRest() const;
143 void SetSixAxisEnabled(bool six_axis_status);
142 LedPattern GetLedPattern(u32 npad_id); 144 LedPattern GetLedPattern(u32 npad_id);
143 void SetVibrationEnabled(bool can_vibrate); 145 void SetVibrationEnabled(bool can_vibrate);
144 bool IsVibrationEnabled() const; 146 bool IsVibrationEnabled() const;
@@ -261,6 +263,24 @@ private:
261 }; 263 };
262 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); 264 static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
263 265
266 struct SixAxisStates {
267 s64_le timestamp{};
268 INSERT_PADDING_WORDS(2);
269 s64_le timestamp2{};
270 Common::Vec3f accel{};
271 Common::Vec3f gyro{};
272 Common::Vec3f rotation{};
273 std::array<Common::Vec3f, 3> orientation{};
274 s64_le always_one{1};
275 };
276 static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
277
278 struct SixAxisGeneric {
279 CommonHeader common{};
280 std::array<SixAxisStates, 17> sixaxis{};
281 };
282 static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
283
264 enum class ColorReadError : u32_le { 284 enum class ColorReadError : u32_le {
265 ReadOk = 0, 285 ReadOk = 0,
266 ColorDoesntExist = 1, 286 ColorDoesntExist = 1,
@@ -290,6 +310,13 @@ private:
290 }; 310 };
291 }; 311 };
292 312
313 struct MotionDevice {
314 Common::Vec3f accel;
315 Common::Vec3f gyro;
316 Common::Vec3f rotation;
317 std::array<Common::Vec3f, 3> orientation;
318 };
319
293 struct NPadEntry { 320 struct NPadEntry {
294 NPadType joy_styles; 321 NPadType joy_styles;
295 NPadAssignments pad_assignment; 322 NPadAssignments pad_assignment;
@@ -309,9 +336,12 @@ private:
309 NPadGeneric pokeball_states; 336 NPadGeneric pokeball_states;
310 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be 337 NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
311 // relying on this for the time being 338 // relying on this for the time being
312 INSERT_PADDING_BYTES( 339 SixAxisGeneric sixaxis_full;
313 0x708 * 340 SixAxisGeneric sixaxis_handheld;
314 6); // TODO(ogniK): SixAxis states, require more information before implementation 341 SixAxisGeneric sixaxis_dual_left;
342 SixAxisGeneric sixaxis_dual_right;
343 SixAxisGeneric sixaxis_left;
344 SixAxisGeneric sixaxis_right;
315 NPadDevice device_type; 345 NPadDevice device_type;
316 NPadProperties properties; 346 NPadProperties properties;
317 INSERT_PADDING_WORDS(1); 347 INSERT_PADDING_WORDS(1);
@@ -334,14 +364,18 @@ private:
334 364
335 NPadType style{}; 365 NPadType style{};
336 std::array<NPadEntry, 10> shared_memory_entries{}; 366 std::array<NPadEntry, 10> shared_memory_entries{};
337 std::array< 367 using ButtonArray = std::array<
338 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, 368 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
339 10> 369 10>;
340 buttons; 370 using StickArray = std::array<
341 std::array<
342 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, 371 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
343 10> 372 10>;
344 sticks; 373 using MotionArray = std::array<
374 std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>,
375 10>;
376 ButtonArray buttons;
377 StickArray sticks;
378 MotionArray motions;
345 std::vector<u32> supported_npad_id_types{}; 379 std::vector<u32> supported_npad_id_types{};
346 NpadHoldType hold_type{NpadHoldType::Vertical}; 380 NpadHoldType hold_type{NpadHoldType::Vertical};
347 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 381 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
@@ -351,6 +385,8 @@ private:
351 std::array<ControllerHolder, 10> connected_controllers{}; 385 std::array<ControllerHolder, 10> connected_controllers{};
352 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 386 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
353 bool can_controllers_vibrate{true}; 387 bool can_controllers_vibrate{true};
388 bool sixaxis_sensors_enabled{true};
389 bool sixaxis_at_rest{true};
354 std::array<ControllerPad, 10> npad_pad_states{}; 390 std::array<ControllerPad, 10> npad_pad_states{};
355 bool is_in_lr_assignment_mode{false}; 391 bool is_in_lr_assignment_mode{false};
356 Core::System& system; 392 Core::System& system;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d300ce25d..395e83b3f 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -40,7 +40,7 @@ namespace Service::HID {
40// Updating period for each HID device. 40// Updating period for each HID device.
41// HID is polled every 15ms, this value was derived from 41// HID is polled every 15ms, this value was derived from
42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet 42// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
43constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz) 43constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 44constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
45 45
46IAppletResource::IAppletResource(Core::System& system) 46IAppletResource::IAppletResource(Core::System& system)
@@ -164,8 +164,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
164 {56, nullptr, "ActivateJoyXpad"}, 164 {56, nullptr, "ActivateJoyXpad"},
165 {58, nullptr, "GetJoyXpadLifoHandle"}, 165 {58, nullptr, "GetJoyXpadLifoHandle"},
166 {59, nullptr, "GetJoyXpadIds"}, 166 {59, nullptr, "GetJoyXpadIds"},
167 {60, nullptr, "ActivateSixAxisSensor"}, 167 {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
168 {61, nullptr, "DeactivateSixAxisSensor"}, 168 {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
169 {62, nullptr, "GetSixAxisSensorLifoHandle"}, 169 {62, nullptr, "GetSixAxisSensorLifoHandle"},
170 {63, nullptr, "ActivateJoySixAxisSensor"}, 170 {63, nullptr, "ActivateJoySixAxisSensor"},
171 {64, nullptr, "DeactivateJoySixAxisSensor"}, 171 {64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -329,6 +329,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
329 rb.Push(0); 329 rb.Push(0);
330} 330}
331 331
332void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto handle{rp.Pop<u32>()};
335 const auto applet_resource_user_id{rp.Pop<u64>()};
336 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
337 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
338 applet_resource_user_id);
339
340 IPC::ResponseBuilder rb{ctx, 2};
341 rb.Push(RESULT_SUCCESS);
342}
343
344void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
345 IPC::RequestParser rp{ctx};
346 const auto handle{rp.Pop<u32>()};
347 const auto applet_resource_user_id{rp.Pop<u64>()};
348 applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
349
350 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
351 applet_resource_user_id);
352
353 IPC::ResponseBuilder rb{ctx, 2};
354 rb.Push(RESULT_SUCCESS);
355}
356
332void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { 357void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx}; 358 IPC::RequestParser rp{ctx};
334 const auto applet_resource_user_id{rp.Pop<u64>()}; 359 const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -484,13 +509,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
484 const auto handle{rp.Pop<u32>()}; 509 const auto handle{rp.Pop<u32>()};
485 const auto applet_resource_user_id{rp.Pop<u64>()}; 510 const auto applet_resource_user_id{rp.Pop<u64>()};
486 511
487 LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, 512 LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
488 applet_resource_user_id); 513 applet_resource_user_id);
489 514
490 IPC::ResponseBuilder rb{ctx, 3}; 515 IPC::ResponseBuilder rb{ctx, 3};
491 rb.Push(RESULT_SUCCESS); 516 rb.Push(RESULT_SUCCESS);
492 // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. 517 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
493 rb.Push(true); 518 .IsSixAxisSensorAtRest());
494} 519}
495 520
496void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { 521void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index efb07547f..e04aaf1e9 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -86,6 +86,8 @@ private:
86 void CreateAppletResource(Kernel::HLERequestContext& ctx); 86 void CreateAppletResource(Kernel::HLERequestContext& ctx);
87 void ActivateXpad(Kernel::HLERequestContext& ctx); 87 void ActivateXpad(Kernel::HLERequestContext& ctx);
88 void GetXpadIDs(Kernel::HLERequestContext& ctx); 88 void GetXpadIDs(Kernel::HLERequestContext& ctx);
89 void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
90 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
89 void ActivateDebugPad(Kernel::HLERequestContext& ctx); 91 void ActivateDebugPad(Kernel::HLERequestContext& ctx);
90 void ActivateTouchScreen(Kernel::HLERequestContext& ctx); 92 void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
91 void ActivateMouse(Kernel::HLERequestContext& ctx); 93 void ActivateMouse(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 5e2d769a4..a0469ffbd 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,6 +2,7 @@
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 <array>
5#include <atomic> 6#include <atomic>
6 7
7#include "common/logging/log.h" 8#include "common/logging/log.h"
@@ -72,10 +73,10 @@ private:
72 std::array<u8, 10> uuid; 73 std::array<u8, 10> uuid;
73 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it 74 u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
74 // mean something else 75 // mean something else
75 INSERT_PADDING_BYTES(0x15); 76 std::array<u8, 0x15> padding_1;
76 u32_le protocol; 77 u32_le protocol;
77 u32_le tag_type; 78 u32_le tag_type;
78 INSERT_PADDING_BYTES(0x2c); 79 std::array<u8, 0x2c> padding_2;
79 }; 80 };
80 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); 81 static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
81 82
@@ -213,13 +214,15 @@ private:
213 LOG_DEBUG(Service_NFP, "called"); 214 LOG_DEBUG(Service_NFP, "called");
214 215
215 IPC::ResponseBuilder rb{ctx, 2}; 216 IPC::ResponseBuilder rb{ctx, 2};
216 auto amiibo = nfp_interface.GetAmiiboBuffer(); 217 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
217 TagInfo tag_info{}; 218 const TagInfo tag_info{
218 tag_info.uuid = amiibo.uuid; 219 .uuid = amiibo.uuid,
219 tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); 220 .uuid_length = static_cast<u8>(tag_info.uuid.size()),
220 221 .padding_1 = {},
221 tag_info.protocol = 1; // TODO(ogniK): Figure out actual values 222 .protocol = 1, // TODO(ogniK): Figure out actual values
222 tag_info.tag_type = 2; 223 .tag_type = 2,
224 .padding_2 = {},
225 };
223 ctx.WriteBuffer(tag_info); 226 ctx.WriteBuffer(tag_info);
224 rb.Push(RESULT_SUCCESS); 227 rb.Push(RESULT_SUCCESS);
225 } 228 }
@@ -236,7 +239,7 @@ private:
236 LOG_DEBUG(Service_NFP, "called"); 239 LOG_DEBUG(Service_NFP, "called");
237 240
238 IPC::ResponseBuilder rb{ctx, 2}; 241 IPC::ResponseBuilder rb{ctx, 2};
239 auto amiibo = nfp_interface.GetAmiiboBuffer(); 242 const auto& amiibo = nfp_interface.GetAmiiboBuffer();
240 ctx.WriteBuffer(amiibo.model_info); 243 ctx.WriteBuffer(amiibo.model_info);
241 rb.Push(RESULT_SUCCESS); 244 rb.Push(RESULT_SUCCESS);
242 } 245 }
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 538f28495..76b3533ec 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -72,25 +72,6 @@
72 72
73namespace Service { 73namespace Service {
74 74
75/**
76 * Creates a function string for logging, complete with the name (or header code, depending
77 * on what's passed in) the port name, and all the cmd_buff arguments.
78 */
79[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
80 std::string_view port_name,
81 const u32* cmd_buff) {
82 // Number of params == bits 0-5 + bits 6-11
83 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
84
85 std::string function_string = fmt::format("function '{}': port={}", name, port_name);
86 for (int i = 1; i <= num_params; ++i) {
87 function_string += fmt::format(", cmd_buff[{}]=0x{:X}", i, cmd_buff[i]);
88 }
89 return function_string;
90}
91
92////////////////////////////////////////////////////////////////////////////////////////////////////
93
94ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, 75ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
95 InvokerFn* handler_invoker) 76 InvokerFn* handler_invoker)
96 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} 77 : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
@@ -105,10 +86,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
105 port_installed = true; 86 port_installed = true;
106} 87}
107 88
108void ServiceFrameworkBase::InstallAsNamedPort() { 89void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
109 ASSERT(!port_installed); 90 ASSERT(!port_installed);
110 91
111 auto& kernel = Core::System::GetInstance().Kernel();
112 auto [server_port, client_port] = 92 auto [server_port, client_port] =
113 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); 93 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
114 server_port->SetHleHandler(shared_from_this()); 94 server_port->SetHleHandler(shared_from_this());
@@ -116,10 +96,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
116 port_installed = true; 96 port_installed = true;
117} 97}
118 98
119std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { 99std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
120 ASSERT(!port_installed); 100 ASSERT(!port_installed);
121 101
122 auto& kernel = Core::System::GetInstance().Kernel();
123 auto [server_port, client_port] = 102 auto [server_port, client_port] =
124 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); 103 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
125 auto port = MakeResult(std::move(server_port)).Unwrap(); 104 auto port = MakeResult(std::move(server_port)).Unwrap();
@@ -191,9 +170,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
191 return RESULT_SUCCESS; 170 return RESULT_SUCCESS;
192} 171}
193 172
194////////////////////////////////////////////////////////////////////////////////////////////////////
195// Module interface
196
197/// Initialize ServiceManager 173/// Initialize ServiceManager
198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 174void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 175 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 022d885b6..a01ef3353 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -63,9 +63,9 @@ public:
63 /// Creates a port pair and registers this service with the given ServiceManager. 63 /// Creates a port pair and registers this service with the given ServiceManager.
64 void InstallAsService(SM::ServiceManager& service_manager); 64 void InstallAsService(SM::ServiceManager& service_manager);
65 /// Creates a port pair and registers it on the kernel's global port registry. 65 /// Creates a port pair and registers it on the kernel's global port registry.
66 void InstallAsNamedPort(); 66 void InstallAsNamedPort(Kernel::KernelCore& kernel);
67 /// Creates and returns an unregistered port for the service. 67 /// Creates and returns an unregistered port for the service.
68 std::shared_ptr<Kernel::ClientPort> CreatePort(); 68 std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
69 69
70 void InvokeRequest(Kernel::HLERequestContext& ctx); 70 void InvokeRequest(Kernel::HLERequestContext& ctx);
71 71
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index d872de16c..9c1da361b 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -19,7 +19,7 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); 19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); 20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
21 21
22ServiceManager::ServiceManager() = default; 22ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
23ServiceManager::~ServiceManager() = default; 23ServiceManager::~ServiceManager() = default;
24 24
25void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { 25void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
@@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
27} 27}
28 28
29static ResultCode ValidateServiceName(const std::string& name) { 29static ResultCode ValidateServiceName(const std::string& name) {
30 if (name.size() <= 0 || name.size() > 8) { 30 if (name.empty() || name.size() > 8) {
31 LOG_ERROR(Service_SM, "Invalid service name! service={}", name); 31 LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
32 return ERR_INVALID_NAME; 32 return ERR_INVALID_NAME;
33 } 33 }
34 if (name.find('\0') != std::string::npos) { 34 if (name.rfind('\0') != std::string::npos) {
35 LOG_ERROR(Service_SM, "A non null terminated service was passed"); 35 LOG_ERROR(Service_SM, "A non null terminated service was passed");
36 return ERR_INVALID_NAME; 36 return ERR_INVALID_NAME;
37 } 37 }
@@ -43,13 +43,13 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self,
43 ASSERT(self->sm_interface.expired()); 43 ASSERT(self->sm_interface.expired());
44 44
45 auto sm = std::make_shared<SM>(self, kernel); 45 auto sm = std::make_shared<SM>(self, kernel);
46 sm->InstallAsNamedPort(); 46 sm->InstallAsNamedPort(kernel);
47 self->sm_interface = sm; 47 self->sm_interface = sm;
48 self->controller_interface = std::make_unique<Controller>(); 48 self->controller_interface = std::make_unique<Controller>();
49} 49}
50 50
51ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( 51ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name,
52 std::string name, unsigned int max_sessions) { 52 u32 max_sessions) {
53 53
54 CASCADE_CODE(ValidateServiceName(name)); 54 CASCADE_CODE(ValidateServiceName(name));
55 55
@@ -58,7 +58,6 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(
58 return ERR_ALREADY_REGISTERED; 58 return ERR_ALREADY_REGISTERED;
59 } 59 }
60 60
61 auto& kernel = Core::System::GetInstance().Kernel();
62 auto [server_port, client_port] = 61 auto [server_port, client_port] =
63 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); 62 Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
64 63
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index aabf166b7..6790c86f0 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -48,11 +48,11 @@ class ServiceManager {
48public: 48public:
49 static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); 49 static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel);
50 50
51 ServiceManager(); 51 explicit ServiceManager(Kernel::KernelCore& kernel_);
52 ~ServiceManager(); 52 ~ServiceManager();
53 53
54 ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name, 54 ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name,
55 unsigned int max_sessions); 55 u32 max_sessions);
56 ResultCode UnregisterService(const std::string& name); 56 ResultCode UnregisterService(const std::string& name);
57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); 57 ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); 58 ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
@@ -79,6 +79,9 @@ private:
79 79
80 /// Map of registered services, retrieved using GetServicePort or ConnectToService. 80 /// Map of registered services, retrieved using GetServicePort or ConnectToService.
81 std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; 81 std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
82
83 /// Kernel context
84 Kernel::KernelCore& kernel;
82}; 85};
83 86
84} // namespace Service::SM 87} // namespace Service::SM
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 134e83412..394a1bf26 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -89,7 +89,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
89} 89}
90 90
91AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( 91AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
92 Kernel::Process& process) { 92 Kernel::Process& process, Core::System& system) {
93 if (is_loaded) { 93 if (is_loaded) {
94 return {ResultStatus::ErrorAlreadyLoaded, {}}; 94 return {ResultStatus::ErrorAlreadyLoaded, {}};
95 } 95 }
@@ -141,9 +141,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
141 continue; 141 continue;
142 } 142 }
143 143
144 const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; 144 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
145 const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( 145 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
146 process, *module_file, code_size, should_pass_arguments, false)}; 146 process, system, *module_file, code_size, should_pass_arguments, false);
147 if (!tentative_next_load_addr) { 147 if (!tentative_next_load_addr) {
148 return {ResultStatus::ErrorLoadingNSO, {}}; 148 return {ResultStatus::ErrorLoadingNSO, {}};
149 } 149 }
@@ -168,9 +168,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
168 } 168 }
169 169
170 const VAddr load_addr{next_load_addr}; 170 const VAddr load_addr{next_load_addr};
171 const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; 171 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
172 const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( 172 const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
173 process, *module_file, load_addr, should_pass_arguments, true, pm)}; 173 process, system, *module_file, load_addr, should_pass_arguments, true, pm);
174 if (!tentative_next_load_addr) { 174 if (!tentative_next_load_addr) {
175 return {ResultStatus::ErrorLoadingNSO, {}}; 175 return {ResultStatus::ErrorLoadingNSO, {}};
176 } 176 }
@@ -192,8 +192,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
192 // Register the RomFS if a ".romfs" file was found 192 // Register the RomFS if a ".romfs" file was found
193 if (romfs_iter != files.end() && *romfs_iter != nullptr) { 193 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
194 romfs = *romfs_iter; 194 romfs = *romfs_iter;
195 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 195 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
196 std::make_unique<FileSys::RomFSFactory>(*this)); 196 *this, system.GetContentProvider(), system.GetFileSystemController()));
197 } 197 }
198 198
199 is_loaded = true; 199 is_loaded = true;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1c0a354a4..35d340317 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/program_metadata.h" 9#include "core/file_sys/program_metadata.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace Loader { 16namespace Loader {
13 17
14/** 18/**
@@ -37,7 +41,7 @@ public:
37 return IdentifyType(file); 41 return IdentifyType(file);
38 } 42 }
39 43
40 LoadResult Load(Kernel::Process& process) override; 44 LoadResult Load(Kernel::Process& process, Core::System& system) override;
41 45
42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 46 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
43 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 47 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 8f7615115..dca1fcb18 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -383,7 +383,8 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
383 return FileType::Error; 383 return FileType::Error;
384} 384}
385 385
386AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { 386AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process,
387 [[maybe_unused]] Core::System& system) {
387 if (is_loaded) { 388 if (is_loaded) {
388 return {ResultStatus::ErrorAlreadyLoaded, {}}; 389 return {ResultStatus::ErrorAlreadyLoaded, {}};
389 } 390 }
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 7ef7770a6..3527933ad 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -8,6 +8,10 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace Loader { 15namespace Loader {
12 16
13/// Loads an ELF/AXF file 17/// Loads an ELF/AXF file
@@ -26,7 +30,7 @@ public:
26 return IdentifyType(file); 30 return IdentifyType(file);
27 } 31 }
28 32
29 LoadResult Load(Kernel::Process& process) override; 33 LoadResult Load(Kernel::Process& process, Core::System& system) override;
30}; 34};
31 35
32} // namespace Loader 36} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 40fa03ad1..5981bcd21 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -43,7 +43,8 @@ FileType AppLoader_KIP::GetFileType() const {
43 : FileType::Error; 43 : FileType::Error;
44} 44}
45 45
46AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { 46AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
47 [[maybe_unused]] Core::System& system) {
47 if (is_loaded) { 48 if (is_loaded) {
48 return {ResultStatus::ErrorAlreadyLoaded, {}}; 49 return {ResultStatus::ErrorAlreadyLoaded, {}};
49 } 50 }
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
index 12ca40269..dee05a7b5 100644
--- a/src/core/loader/kip.h
+++ b/src/core/loader/kip.h
@@ -6,6 +6,10 @@
6 6
7#include "core/loader/loader.h" 7#include "core/loader/loader.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace FileSys { 13namespace FileSys {
10class KIP; 14class KIP;
11} 15}
@@ -26,7 +30,7 @@ public:
26 30
27 FileType GetFileType() const override; 31 FileType GetFileType() const override;
28 32
29 LoadResult Load(Kernel::Process& process) override; 33 LoadResult Load(Kernel::Process& process, Core::System& system) override;
30 34
31private: 35private:
32 std::unique_ptr<FileSys::KIP> kip; 36 std::unique_ptr<FileSys::KIP> kip;
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 227ecc704..ac60b097a 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -15,6 +15,10 @@
15#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/vfs.h" 16#include "core/file_sys/vfs.h"
17 17
18namespace Core {
19class System;
20}
21
18namespace FileSys { 22namespace FileSys {
19class NACP; 23class NACP;
20} // namespace FileSys 24} // namespace FileSys
@@ -154,9 +158,10 @@ public:
154 /** 158 /**
155 * Load the application and return the created Process instance 159 * Load the application and return the created Process instance
156 * @param process The newly created process. 160 * @param process The newly created process.
161 * @param system The system that this process is being loaded under.
157 * @return The status result of the operation. 162 * @return The status result of the operation.
158 */ 163 */
159 virtual LoadResult Load(Kernel::Process& process) = 0; 164 virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0;
160 165
161 /** 166 /**
162 * Get the code (typically .code section) of the application 167 * Get the code (typically .code section) of the application
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index a152981a0..49028177b 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,7 +41,8 @@ FileType AppLoader_NAX::GetFileType() const {
41 return IdentifyTypeImpl(*nax); 41 return IdentifyTypeImpl(*nax);
42} 42}
43 43
44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { 44AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process,
45 [[maybe_unused]] Core::System& system) {
45 if (is_loaded) { 46 if (is_loaded) {
46 return {ResultStatus::ErrorAlreadyLoaded, {}}; 47 return {ResultStatus::ErrorAlreadyLoaded, {}};
47 } 48 }
@@ -65,7 +66,7 @@ AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
65 return {nca_status, {}}; 66 return {nca_status, {}};
66 } 67 }
67 68
68 const auto result = nca_loader->Load(process); 69 const auto result = nca_loader->Load(process, system);
69 if (result.first != ResultStatus::Success) { 70 if (result.first != ResultStatus::Success) {
70 return result; 71 return result;
71 } 72 }
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index eaec9bf58..c2b7722b5 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -8,10 +8,12 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace FileSys { 11namespace Core {
12class System;
13}
12 14
15namespace FileSys {
13class NAX; 16class NAX;
14
15} // namespace FileSys 17} // namespace FileSys
16 18
17namespace Loader { 19namespace Loader {
@@ -33,7 +35,7 @@ public:
33 35
34 FileType GetFileType() const override; 36 FileType GetFileType() const override;
35 37
36 LoadResult Load(Kernel::Process& process) override; 38 LoadResult Load(Kernel::Process& process, Core::System& system) override;
37 39
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 40 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 41 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 5a0469978..fa694de37 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -31,7 +31,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
31 return FileType::Error; 31 return FileType::Error;
32} 32}
33 33
34AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { 34AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) {
35 if (is_loaded) { 35 if (is_loaded) {
36 return {ResultStatus::ErrorAlreadyLoaded, {}}; 36 return {ResultStatus::ErrorAlreadyLoaded, {}};
37 } 37 }
@@ -52,14 +52,14 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
52 52
53 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); 53 directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
54 54
55 const auto load_result = directory_loader->Load(process); 55 const auto load_result = directory_loader->Load(process, system);
56 if (load_result.first != ResultStatus::Success) { 56 if (load_result.first != ResultStatus::Success) {
57 return load_result; 57 return load_result;
58 } 58 }
59 59
60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { 60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
61 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 61 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
62 std::make_unique<FileSys::RomFSFactory>(*this)); 62 *this, system.GetContentProvider(), system.GetFileSystemController()));
63 } 63 }
64 64
65 is_loaded = true; 65 is_loaded = true;
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index e47dc0e47..711070294 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -8,6 +8,10 @@
8#include "core/file_sys/vfs.h" 8#include "core/file_sys/vfs.h"
9#include "core/loader/loader.h" 9#include "core/loader/loader.h"
10 10
11namespace Core {
12class System;
13}
14
11namespace FileSys { 15namespace FileSys {
12class NCA; 16class NCA;
13} 17}
@@ -33,7 +37,7 @@ public:
33 return IdentifyType(file); 37 return IdentifyType(file);
34 } 38 }
35 39
36 LoadResult Load(Kernel::Process& process) override; 40 LoadResult Load(Kernel::Process& process, Core::System& system) override;
37 41
38 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 42 ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
39 u64 ReadRomFSIVFCOffset() const override; 43 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 906544bc9..9fb5eddad 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -208,7 +208,7 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
208 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName()); 208 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
209} 209}
210 210
211AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { 211AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) {
212 if (is_loaded) { 212 if (is_loaded) {
213 return {ResultStatus::ErrorAlreadyLoaded, {}}; 213 return {ResultStatus::ErrorAlreadyLoaded, {}};
214 } 214 }
@@ -218,8 +218,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
218 } 218 }
219 219
220 if (romfs != nullptr) { 220 if (romfs != nullptr) {
221 Core::System::GetInstance().GetFileSystemController().RegisterRomFS( 221 system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
222 std::make_unique<FileSys::RomFSFactory>(*this)); 222 *this, system.GetContentProvider(), system.GetFileSystemController()));
223 } 223 }
224 224
225 is_loaded = true; 225 is_loaded = true;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 4593d48fb..a2aab2ecc 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -10,6 +10,10 @@
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "core/loader/loader.h" 11#include "core/loader/loader.h"
12 12
13namespace Core {
14class System;
15}
16
13namespace FileSys { 17namespace FileSys {
14class NACP; 18class NACP;
15} 19}
@@ -37,7 +41,7 @@ public:
37 return IdentifyType(file); 41 return IdentifyType(file);
38 } 42 }
39 43
40 LoadResult Load(Kernel::Process& process) override; 44 LoadResult Load(Kernel::Process& process, Core::System& system) override;
41 45
42 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 46 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
43 ResultStatus ReadProgramId(u64& out_program_id) override; 47 ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 575330a86..60373cc5f 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -71,7 +71,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
71 return FileType::NSO; 71 return FileType::NSO;
72} 72}
73 73
74std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, 74std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system,
75 const FileSys::VfsFile& file, VAddr load_base, 75 const FileSys::VfsFile& file, VAddr load_base,
76 bool should_pass_arguments, bool load_into_process, 76 bool should_pass_arguments, bool load_into_process,
77 std::optional<FileSys::PatchManager> pm) { 77 std::optional<FileSys::PatchManager> pm) {
@@ -148,7 +148,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
148 148
149 // Apply cheats if they exist and the program has a valid title ID 149 // Apply cheats if they exist and the program has a valid title ID
150 if (pm) { 150 if (pm) {
151 auto& system = Core::System::GetInstance();
152 system.SetCurrentProcessBuildID(nso_header.build_id); 151 system.SetCurrentProcessBuildID(nso_header.build_id);
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 152 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) { 153 if (!cheats.empty()) {
@@ -166,7 +165,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
166 return load_base + image_size; 165 return load_base + image_size;
167} 166}
168 167
169AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { 168AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) {
170 if (is_loaded) { 169 if (is_loaded) {
171 return {ResultStatus::ErrorAlreadyLoaded, {}}; 170 return {ResultStatus::ErrorAlreadyLoaded, {}};
172 } 171 }
@@ -175,7 +174,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
175 174
176 // Load module 175 // Load module
177 const VAddr base_address = process.PageTable().GetCodeRegionStart(); 176 const VAddr base_address = process.PageTable().GetCodeRegionStart();
178 if (!LoadModule(process, *file, base_address, true, true)) { 177 if (!LoadModule(process, system, *file, base_address, true, true)) {
179 return {ResultStatus::ErrorLoadingNSO, {}}; 178 return {ResultStatus::ErrorLoadingNSO, {}};
180 } 179 }
181 180
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index b210830f0..4bd47787d 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -12,6 +12,10 @@
12#include "core/file_sys/patch_manager.h" 12#include "core/file_sys/patch_manager.h"
13#include "core/loader/loader.h" 13#include "core/loader/loader.h"
14 14
15namespace Core {
16class System;
17}
18
15namespace Kernel { 19namespace Kernel {
16class Process; 20class Process;
17} 21}
@@ -80,12 +84,12 @@ public:
80 return IdentifyType(file); 84 return IdentifyType(file);
81 } 85 }
82 86
83 static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, 87 static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system,
84 VAddr load_base, bool should_pass_arguments, 88 const FileSys::VfsFile& file, VAddr load_base,
85 bool load_into_process, 89 bool should_pass_arguments, bool load_into_process,
86 std::optional<FileSys::PatchManager> pm = {}); 90 std::optional<FileSys::PatchManager> pm = {});
87 91
88 LoadResult Load(Kernel::Process& process) override; 92 LoadResult Load(Kernel::Process& process, Core::System& system) override;
89 93
90 ResultStatus ReadNSOModules(Modules& modules) override; 94 ResultStatus ReadNSOModules(Modules& modules) override;
91 95
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 13950fc08..15e528fa8 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -71,7 +71,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
71 return FileType::Error; 71 return FileType::Error;
72} 72}
73 73
74AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { 74AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) {
75 if (is_loaded) { 75 if (is_loaded) {
76 return {ResultStatus::ErrorAlreadyLoaded, {}}; 76 return {ResultStatus::ErrorAlreadyLoaded, {}};
77 } 77 }
@@ -99,15 +99,14 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 99 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
100 } 100 }
101 101
102 const auto result = secondary_loader->Load(process); 102 const auto result = secondary_loader->Load(process, system);
103 if (result.first != ResultStatus::Success) { 103 if (result.first != ResultStatus::Success) {
104 return result; 104 return result;
105 } 105 }
106 106
107 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
109 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( 109 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
110 std::move(update_raw));
111 } 110 }
112 111
113 is_loaded = true; 112 is_loaded = true;
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 868b028d3..b27deb686 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace FileSys { 16namespace FileSys {
13class NACP; 17class NACP;
14class NSP; 18class NSP;
@@ -35,7 +39,7 @@ public:
35 return IdentifyType(file); 39 return IdentifyType(file);
36 } 40 }
37 41
38 LoadResult Load(Kernel::Process& process) override; 42 LoadResult Load(Kernel::Process& process, Core::System& system) override;
39 43
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 44 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 45 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7186ad1ff..25e83af0f 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -49,7 +49,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
49 return FileType::Error; 49 return FileType::Error;
50} 50}
51 51
52AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { 52AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) {
53 if (is_loaded) { 53 if (is_loaded) {
54 return {ResultStatus::ErrorAlreadyLoaded, {}}; 54 return {ResultStatus::ErrorAlreadyLoaded, {}};
55 } 55 }
@@ -66,15 +66,14 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
66 return {ResultStatus::ErrorMissingProductionKeyFile, {}}; 66 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
67 } 67 }
68 68
69 const auto result = nca_loader->Load(process); 69 const auto result = nca_loader->Load(process, system);
70 if (result.first != ResultStatus::Success) { 70 if (result.first != ResultStatus::Success) {
71 return result; 71 return result;
72 } 72 }
73 73
74 FileSys::VirtualFile update_raw; 74 FileSys::VirtualFile update_raw;
75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
76 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( 76 system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
77 std::move(update_raw));
78 } 77 }
79 78
80 is_loaded = true; 79 is_loaded = true;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 618ae2f47..04aea286f 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -9,6 +9,10 @@
9#include "core/file_sys/vfs.h" 9#include "core/file_sys/vfs.h"
10#include "core/loader/loader.h" 10#include "core/loader/loader.h"
11 11
12namespace Core {
13class System;
14}
15
12namespace FileSys { 16namespace FileSys {
13class NACP; 17class NACP;
14class XCI; 18class XCI;
@@ -35,7 +39,7 @@ public:
35 return IdentifyType(file); 39 return IdentifyType(file);
36 } 40 }
37 41
38 LoadResult Load(Kernel::Process& process) override; 42 LoadResult Load(Kernel::Process& process, Core::System& system) override;
39 43
40 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 44 ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
41 u64 ReadRomFSIVFCOffset() const override; 45 u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index e503118dd..29284a42d 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,10 +19,24 @@
19#include "core/memory/cheat_engine.h" 19#include "core/memory/cheat_engine.h"
20 20
21namespace Core::Memory { 21namespace Core::Memory {
22 22namespace {
23constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; 23constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; 24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
25 25
26std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
27 auto end_index = start_index;
28 while (data[end_index] != match) {
29 ++end_index;
30 if (end_index > data.size() ||
31 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
32 return {};
33 }
34 }
35
36 return data.substr(start_index, end_index - start_index);
37}
38} // Anonymous namespace
39
26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) 40StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
27 : metadata(metadata), system(system) {} 41 : metadata(metadata), system(system) {}
28 42
@@ -82,26 +96,9 @@ CheatParser::~CheatParser() = default;
82 96
83TextCheatParser::~TextCheatParser() = default; 97TextCheatParser::~TextCheatParser() = default;
84 98
85namespace { 99std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
86template <char match>
87std::string_view ExtractName(std::string_view data, std::size_t start_index) {
88 auto end_index = start_index;
89 while (data[end_index] != match) {
90 ++end_index;
91 if (end_index > data.size() ||
92 (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
93 return {};
94 }
95 }
96
97 return data.substr(start_index, end_index - start_index);
98}
99} // Anonymous namespace
100
101std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
102 std::string_view data) const {
103 std::vector<CheatEntry> out(1); 100 std::vector<CheatEntry> out(1);
104 std::optional<u64> current_entry = std::nullopt; 101 std::optional<u64> current_entry;
105 102
106 for (std::size_t i = 0; i < data.size(); ++i) { 103 for (std::size_t i = 0; i < data.size(); ++i) {
107 if (::isspace(data[i])) { 104 if (::isspace(data[i])) {
@@ -115,7 +112,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
115 return {}; 112 return {};
116 } 113 }
117 114
118 const auto name = ExtractName<'}'>(data, i + 1); 115 const auto name = ExtractName(data, i + 1, '}');
119 if (name.empty()) { 116 if (name.empty()) {
120 return {}; 117 return {};
121 } 118 }
@@ -132,7 +129,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
132 current_entry = out.size(); 129 current_entry = out.size();
133 out.emplace_back(); 130 out.emplace_back();
134 131
135 const auto name = ExtractName<']'>(data, i + 1); 132 const auto name = ExtractName(data, i + 1, ']');
136 if (name.empty()) { 133 if (name.empty()) {
137 return {}; 134 return {};
138 } 135 }
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index fa039a831..a31002346 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -47,8 +47,7 @@ class CheatParser {
47public: 47public:
48 virtual ~CheatParser(); 48 virtual ~CheatParser();
49 49
50 virtual std::vector<CheatEntry> Parse(const Core::System& system, 50 [[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
51 std::string_view data) const = 0;
52}; 51};
53 52
54// CheatParser implementation that parses text files 53// CheatParser implementation that parses text files
@@ -56,7 +55,7 @@ class TextCheatParser final : public CheatParser {
56public: 55public:
57 ~TextCheatParser() override; 56 ~TextCheatParser() override;
58 57
59 std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override; 58 [[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
60}; 59};
61 60
62// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming 61// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
diff --git a/src/core/settings.h b/src/core/settings.h
index 80f0d95a7..9834f44bb 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -152,6 +152,7 @@ struct Values {
152 152
153 bool vibration_enabled; 153 bool vibration_enabled;
154 154
155 bool motion_enabled;
155 std::string motion_device; 156 std::string motion_device;
156 std::string touch_device; 157 std::string touch_device;
157 TouchscreenInput touchscreen; 158 TouchscreenInput touchscreen;
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index c6c423c4b..c507c9891 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -4,7 +4,16 @@
4 4
5#include <chrono> 5#include <chrono>
6#include <thread> 6#include <thread>
7
8#ifdef _MSC_VER
9#pragma warning(push)
10#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union
11#endif
7#include <libusb.h> 12#include <libusb.h>
13#ifdef _MSC_VER
14#pragma warning(pop)
15#endif
16
8#include "common/logging/log.h" 17#include "common/logging/log.h"
9#include "input_common/gcadapter/gc_adapter.h" 18#include "input_common/gcadapter/gc_adapter.h"
10 19
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index ea1a1cee6..062ec66b5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -12,6 +12,7 @@
12#include "input_common/main.h" 12#include "input_common/main.h"
13#include "input_common/motion_emu.h" 13#include "input_common/motion_emu.h"
14#include "input_common/touch_from_button.h" 14#include "input_common/touch_from_button.h"
15#include "input_common/udp/client.h"
15#include "input_common/udp/udp.h" 16#include "input_common/udp/udp.h"
16#ifdef HAVE_SDL2 17#ifdef HAVE_SDL2
17#include "input_common/sdl/sdl.h" 18#include "input_common/sdl/sdl.h"
@@ -40,7 +41,11 @@ struct InputSubsystem::Impl {
40 sdl = SDL::Init(); 41 sdl = SDL::Init();
41#endif 42#endif
42 43
43 udp = CemuhookUDP::Init(); 44 udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
45 udpmotion = std::make_shared<UDPMotionFactory>(udp);
46 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
47 udptouch = std::make_shared<UDPTouchFactory>(udp);
48 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
44 } 49 }
45 50
46 void Shutdown() { 51 void Shutdown() {
@@ -53,12 +58,17 @@ struct InputSubsystem::Impl {
53#ifdef HAVE_SDL2 58#ifdef HAVE_SDL2
54 sdl.reset(); 59 sdl.reset();
55#endif 60#endif
56 udp.reset();
57 Input::UnregisterFactory<Input::ButtonDevice>("gcpad"); 61 Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
58 Input::UnregisterFactory<Input::AnalogDevice>("gcpad"); 62 Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
59 63
60 gcbuttons.reset(); 64 gcbuttons.reset();
61 gcanalog.reset(); 65 gcanalog.reset();
66
67 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
68 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
69
70 udpmotion.reset();
71 udptouch.reset();
62 } 72 }
63 73
64 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const { 74 [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
@@ -109,14 +119,28 @@ struct InputSubsystem::Impl {
109 return {}; 119 return {};
110 } 120 }
111 121
122 [[nodiscard]] MotionMapping GetMotionMappingForDevice(
123 const Common::ParamPackage& params) const {
124 if (!params.Has("class") || params.Get("class", "") == "any") {
125 return {};
126 }
127 if (params.Get("class", "") == "cemuhookudp") {
128 // TODO return the correct motion device
129 return {};
130 }
131 return {};
132 }
133
112 std::shared_ptr<Keyboard> keyboard; 134 std::shared_ptr<Keyboard> keyboard;
113 std::shared_ptr<MotionEmu> motion_emu; 135 std::shared_ptr<MotionEmu> motion_emu;
114#ifdef HAVE_SDL2 136#ifdef HAVE_SDL2
115 std::unique_ptr<SDL::State> sdl; 137 std::unique_ptr<SDL::State> sdl;
116#endif 138#endif
117 std::unique_ptr<CemuhookUDP::State> udp;
118 std::shared_ptr<GCButtonFactory> gcbuttons; 139 std::shared_ptr<GCButtonFactory> gcbuttons;
119 std::shared_ptr<GCAnalogFactory> gcanalog; 140 std::shared_ptr<GCAnalogFactory> gcanalog;
141 std::shared_ptr<UDPMotionFactory> udpmotion;
142 std::shared_ptr<UDPTouchFactory> udptouch;
143 std::shared_ptr<CemuhookUDP::Client> udp;
120}; 144};
121 145
122InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} 146InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
@@ -175,6 +199,22 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
175 return impl->gcbuttons.get(); 199 return impl->gcbuttons.get();
176} 200}
177 201
202UDPMotionFactory* InputSubsystem::GetUDPMotions() {
203 return impl->udpmotion.get();
204}
205
206const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
207 return impl->udpmotion.get();
208}
209
210UDPTouchFactory* InputSubsystem::GetUDPTouch() {
211 return impl->udptouch.get();
212}
213
214const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
215 return impl->udptouch.get();
216}
217
178void InputSubsystem::ReloadInputDevices() { 218void InputSubsystem::ReloadInputDevices() {
179 if (!impl->udp) { 219 if (!impl->udp) {
180 return; 220 return;
diff --git a/src/input_common/main.h b/src/input_common/main.h
index f3fbf696e..dded3f1ef 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -21,10 +21,14 @@ namespace Settings::NativeButton {
21enum Values : int; 21enum Values : int;
22} 22}
23 23
24namespace Settings::NativeMotion {
25enum Values : int;
26}
27
24namespace InputCommon { 28namespace InputCommon {
25namespace Polling { 29namespace Polling {
26 30
27enum class DeviceType { Button, AnalogPreferred }; 31enum class DeviceType { Button, AnalogPreferred, Motion };
28 32
29/** 33/**
30 * A class that can be used to get inputs from an input device like controllers without having to 34 * A class that can be used to get inputs from an input device like controllers without having to
@@ -50,6 +54,8 @@ public:
50 54
51class GCAnalogFactory; 55class GCAnalogFactory;
52class GCButtonFactory; 56class GCButtonFactory;
57class UDPMotionFactory;
58class UDPTouchFactory;
53class Keyboard; 59class Keyboard;
54class MotionEmu; 60class MotionEmu;
55 61
@@ -59,6 +65,7 @@ class MotionEmu;
59 */ 65 */
60using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; 66using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
61using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; 67using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
68using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
62 69
63class InputSubsystem { 70class InputSubsystem {
64public: 71public:
@@ -103,6 +110,9 @@ public:
103 /// Retrieves the button mappings for the given device. 110 /// Retrieves the button mappings for the given device.
104 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const; 111 [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
105 112
113 /// Retrieves the motion mappings for the given device.
114 [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
115
106 /// Retrieves the underlying GameCube analog handler. 116 /// Retrieves the underlying GameCube analog handler.
107 [[nodiscard]] GCAnalogFactory* GetGCAnalogs(); 117 [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
108 118
@@ -115,6 +125,18 @@ public:
115 /// Retrieves the underlying GameCube button handler. 125 /// Retrieves the underlying GameCube button handler.
116 [[nodiscard]] const GCButtonFactory* GetGCButtons() const; 126 [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
117 127
128 /// Retrieves the underlying udp motion handler.
129 [[nodiscard]] UDPMotionFactory* GetUDPMotions();
130
131 /// Retrieves the underlying udp motion handler.
132 [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
133
134 /// Retrieves the underlying udp touch handler.
135 [[nodiscard]] UDPTouchFactory* GetUDPTouch();
136
137 /// Retrieves the underlying udp touch handler.
138 [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
139
118 /// Reloads the input devices 140 /// Reloads the input devices
119 void ReloadInputDevices(); 141 void ReloadInputDevices();
120 142
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index d4cdf76a3..69fd3c1d2 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -56,7 +56,7 @@ public:
56 is_tilting = false; 56 is_tilting = false;
57 } 57 }
58 58
59 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() { 59 Input::MotionStatus GetStatus() {
60 std::lock_guard guard{status_mutex}; 60 std::lock_guard guard{status_mutex};
61 return status; 61 return status;
62 } 62 }
@@ -76,7 +76,7 @@ private:
76 76
77 Common::Event shutdown_event; 77 Common::Event shutdown_event;
78 78
79 std::tuple<Common::Vec3<float>, Common::Vec3<float>> status; 79 Input::MotionStatus status;
80 std::mutex status_mutex; 80 std::mutex status_mutex;
81 81
82 // Note: always keep the thread declaration at the end so that other objects are initialized 82 // Note: always keep the thread declaration at the end so that other objects are initialized
@@ -113,10 +113,19 @@ private:
113 gravity = QuaternionRotate(inv_q, gravity); 113 gravity = QuaternionRotate(inv_q, gravity);
114 angular_rate = QuaternionRotate(inv_q, angular_rate); 114 angular_rate = QuaternionRotate(inv_q, angular_rate);
115 115
116 // TODO: Calculate the correct rotation vector and orientation matrix
117 const auto matrix4x4 = q.ToMatrix();
118 const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
119 const std::array orientation{
120 Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
121 Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
122 Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
123 };
124
116 // Update the sensor state 125 // Update the sensor state
117 { 126 {
118 std::lock_guard guard{status_mutex}; 127 std::lock_guard guard{status_mutex};
119 status = std::make_tuple(gravity, angular_rate); 128 status = std::make_tuple(gravity, angular_rate, rotation, orientation);
120 } 129 }
121 } 130 }
122 } 131 }
@@ -131,7 +140,7 @@ public:
131 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity); 140 device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
132 } 141 }
133 142
134 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { 143 Input::MotionStatus GetStatus() const override {
135 return device->GetStatus(); 144 return device->GetStatus();
136 } 145 }
137 146
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
index 80c719cf4..b66c05856 100644
--- a/src/input_common/settings.cpp
+++ b/src/input_common/settings.cpp
@@ -14,6 +14,13 @@ const std::array<const char*, NumButtons> mapping = {{
14}}; 14}};
15} 15}
16 16
17namespace NativeMotion {
18const std::array<const char*, NumMotions> mapping = {{
19 "motionleft",
20 "motionright",
21}};
22}
23
17namespace NativeAnalog { 24namespace NativeAnalog {
18const std::array<const char*, NumAnalogs> mapping = {{ 25const std::array<const char*, NumAnalogs> mapping = {{
19 "lstick", 26 "lstick",
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
index 2d258960b..ab0b95cf1 100644
--- a/src/input_common/settings.h
+++ b/src/input_common/settings.h
@@ -66,6 +66,21 @@ constexpr int NUM_STICKS_HID = NumAnalogs;
66extern const std::array<const char*, NumAnalogs> mapping; 66extern const std::array<const char*, NumAnalogs> mapping;
67} // namespace NativeAnalog 67} // namespace NativeAnalog
68 68
69namespace NativeMotion {
70enum Values : int {
71 MOTIONLEFT,
72 MOTIONRIGHT,
73
74 NumMotions,
75};
76
77constexpr int MOTION_HID_BEGIN = MOTIONLEFT;
78constexpr int MOTION_HID_END = NumMotions;
79constexpr int NUM_MOTION_HID = NumMotions;
80
81extern const std::array<const char*, NumMotions> mapping;
82} // namespace NativeMotion
83
69namespace NativeMouseButton { 84namespace NativeMouseButton {
70enum Values { 85enum Values {
71 Left, 86 Left,
@@ -292,6 +307,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
292 307
293using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; 308using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
294using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; 309using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
310using MotionRaw = std::array<std::string, NativeMotion::NumMotions>;
295using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; 311using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
296using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; 312using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
297using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; 313using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
@@ -314,6 +330,7 @@ struct PlayerInput {
314 ControllerType controller_type; 330 ControllerType controller_type;
315 ButtonsRaw buttons; 331 ButtonsRaw buttons;
316 AnalogsRaw analogs; 332 AnalogsRaw analogs;
333 MotionRaw motions;
317 std::string lstick_mod; 334 std::string lstick_mod;
318 std::string rstick_mod; 335 std::string rstick_mod;
319 336
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 3f4eaf448..2b6a68d4b 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -2,14 +2,13 @@
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 <algorithm>
6#include <array>
7#include <chrono> 5#include <chrono>
8#include <cstring> 6#include <cstring>
9#include <functional> 7#include <functional>
10#include <thread> 8#include <thread>
11#include <boost/asio.hpp> 9#include <boost/asio.hpp>
12#include "common/logging/log.h" 10#include "common/logging/log.h"
11#include "core/settings.h"
13#include "input_common/udp/client.h" 12#include "input_common/udp/client.h"
14#include "input_common/udp/protocol.h" 13#include "input_common/udp/protocol.h"
15 14
@@ -131,21 +130,59 @@ static void SocketLoop(Socket* socket) {
131 socket->Loop(); 130 socket->Loop();
132} 131}
133 132
134Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, 133Client::Client() {
135 u8 pad_index, u32 client_id) 134 LOG_INFO(Input, "Udp Initialization started");
136 : status(std::move(status)) { 135 for (std::size_t client = 0; client < clients.size(); client++) {
137 StartCommunication(host, port, pad_index, client_id); 136 u8 pad = client % 4;
137 StartCommunication(client, Settings::values.udp_input_address,
138 Settings::values.udp_input_port, pad, 24872);
139 // Set motion parameters
140 // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
141 // Real HW values are unknown, 0.0001 is an approximate to Standard
142 clients[client].motion.SetGyroThreshold(0.0001f);
143 }
138} 144}
139 145
140Client::~Client() { 146Client::~Client() {
141 socket->Stop(); 147 Reset();
142 thread.join(); 148}
149
150std::vector<Common::ParamPackage> Client::GetInputDevices() const {
151 std::vector<Common::ParamPackage> devices;
152 for (std::size_t client = 0; client < clients.size(); client++) {
153 if (!DeviceConnected(client)) {
154 continue;
155 }
156 std::string name = fmt::format("UDP Controller {}", client);
157 devices.emplace_back(Common::ParamPackage{
158 {"class", "cemuhookudp"},
159 {"display", std::move(name)},
160 {"port", std::to_string(client)},
161 });
162 }
163 return devices;
143} 164}
144 165
166bool Client::DeviceConnected(std::size_t pad) const {
167 // Use last timestamp to detect if the socket has stopped sending data
168 const auto now = std::chrono::system_clock::now();
169 u64 time_difference =
170 std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update)
171 .count();
172 return time_difference < 1000 && clients[pad].active == 1;
173}
174
175void Client::ReloadUDPClient() {
176 for (std::size_t client = 0; client < clients.size(); client++) {
177 ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client);
178 }
179}
145void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 180void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
146 socket->Stop(); 181 // client number must be determined from host / port and pad index
147 thread.join(); 182 std::size_t client = pad_index;
148 StartCommunication(host, port, pad_index, client_id); 183 clients[client].socket->Stop();
184 clients[client].thread.join();
185 StartCommunication(client, host, port, pad_index, client_id);
149} 186}
150 187
151void Client::OnVersion(Response::Version data) { 188void Client::OnVersion(Response::Version data) {
@@ -157,23 +194,39 @@ void Client::OnPortInfo(Response::PortInfo data) {
157} 194}
158 195
159void Client::OnPadData(Response::PadData data) { 196void Client::OnPadData(Response::PadData data) {
197 // client number must be determined from host / port and pad index
198 std::size_t client = data.info.id;
160 LOG_TRACE(Input, "PadData packet received"); 199 LOG_TRACE(Input, "PadData packet received");
161 if (data.packet_counter <= packet_sequence) { 200 if (data.packet_counter == clients[client].packet_sequence) {
162 LOG_WARNING( 201 LOG_WARNING(
163 Input, 202 Input,
164 "PadData packet dropped because its stale info. Current count: {} Packet count: {}", 203 "PadData packet dropped because its stale info. Current count: {} Packet count: {}",
165 packet_sequence, data.packet_counter); 204 clients[client].packet_sequence, data.packet_counter);
166 return; 205 return;
167 } 206 }
168 packet_sequence = data.packet_counter; 207 clients[client].active = data.info.is_pad_active;
169 // TODO: Check how the Switch handles motions and how the CemuhookUDP motion 208 clients[client].packet_sequence = data.packet_counter;
170 // directions correspond to the ones of the Switch 209 const auto now = std::chrono::system_clock::now();
171 Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z); 210 u64 time_difference = std::chrono::duration_cast<std::chrono::microseconds>(
172 Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll); 211 now - clients[client].last_motion_update)
173 { 212 .count();
174 std::lock_guard guard(status->update_mutex); 213 clients[client].last_motion_update = now;
214 Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
215 clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
216 // Gyroscope values are not it the correct scale from better joy.
217 // Dividing by 312 allows us to make one full turn = 1 turn
218 // This must be a configurable valued called sensitivity
219 clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
220 clients[client].motion.UpdateRotation(time_difference);
221 clients[client].motion.UpdateOrientation(time_difference);
222 Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
223 Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
224 Common::Vec3f rotation = clients[client].motion.GetRotations();
225 std::array<Common::Vec3f, 3> orientation = clients[client].motion.GetOrientation();
175 226
176 status->motion_status = {accel, gyro}; 227 {
228 std::lock_guard guard(clients[client].status.update_mutex);
229 clients[client].status.motion_status = {accelerometer, gyroscope, rotation, orientation};
177 230
178 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates 231 // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
179 // between a simple "tap" and a hard press that causes the touch screen to click. 232 // between a simple "tap" and a hard press that causes the touch screen to click.
@@ -182,11 +235,11 @@ void Client::OnPadData(Response::PadData data) {
182 float x = 0; 235 float x = 0;
183 float y = 0; 236 float y = 0;
184 237
185 if (is_active && status->touch_calibration) { 238 if (is_active && clients[client].status.touch_calibration) {
186 const u16 min_x = status->touch_calibration->min_x; 239 const u16 min_x = clients[client].status.touch_calibration->min_x;
187 const u16 max_x = status->touch_calibration->max_x; 240 const u16 max_x = clients[client].status.touch_calibration->max_x;
188 const u16 min_y = status->touch_calibration->min_y; 241 const u16 min_y = clients[client].status.touch_calibration->min_y;
189 const u16 max_y = status->touch_calibration->max_y; 242 const u16 max_y = clients[client].status.touch_calibration->max_y;
190 243
191 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / 244 x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
192 static_cast<float>(max_x - min_x); 245 static_cast<float>(max_x - min_x);
@@ -194,17 +247,80 @@ void Client::OnPadData(Response::PadData data) {
194 static_cast<float>(max_y - min_y); 247 static_cast<float>(max_y - min_y);
195 } 248 }
196 249
197 status->touch_status = {x, y, is_active}; 250 clients[client].status.touch_status = {x, y, is_active};
251
252 if (configuring) {
253 UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
254 }
198 } 255 }
199} 256}
200 257
201void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { 258void Client::StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
259 u32 client_id) {
202 SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, 260 SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
203 [this](Response::PortInfo info) { OnPortInfo(info); }, 261 [this](Response::PortInfo info) { OnPortInfo(info); },
204 [this](Response::PadData data) { OnPadData(data); }}; 262 [this](Response::PadData data) { OnPadData(data); }};
205 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); 263 LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
206 socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); 264 clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
207 thread = std::thread{SocketLoop, this->socket.get()}; 265 clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
266}
267
268void Client::Reset() {
269 for (std::size_t client = 0; client < clients.size(); client++) {
270 clients[client].socket->Stop();
271 clients[client].thread.join();
272 }
273}
274
275void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
276 const Common::Vec3<float>& gyro, bool touch) {
277 UDPPadStatus pad;
278 if (touch) {
279 pad.touch = PadTouch::Click;
280 pad_queue[client].Push(pad);
281 }
282 for (size_t i = 0; i < 3; ++i) {
283 if (gyro[i] > 6.0f || gyro[i] < -6.0f) {
284 pad.motion = static_cast<PadMotion>(i);
285 pad.motion_value = gyro[i];
286 pad_queue[client].Push(pad);
287 }
288 if (acc[i] > 2.0f || acc[i] < -2.0f) {
289 pad.motion = static_cast<PadMotion>(i + 3);
290 pad.motion_value = acc[i];
291 pad_queue[client].Push(pad);
292 }
293 }
294}
295
296void Client::BeginConfiguration() {
297 for (auto& pq : pad_queue) {
298 pq.Clear();
299 }
300 configuring = true;
301}
302
303void Client::EndConfiguration() {
304 for (auto& pq : pad_queue) {
305 pq.Clear();
306 }
307 configuring = false;
308}
309
310DeviceStatus& Client::GetPadState(std::size_t pad) {
311 return clients[pad].status;
312}
313
314const DeviceStatus& Client::GetPadState(std::size_t pad) const {
315 return clients[pad].status;
316}
317
318std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() {
319 return pad_queue;
320}
321
322const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const {
323 return pad_queue;
208} 324}
209 325
210void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, 326void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index b8c654755..523dc6a7a 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -12,8 +12,12 @@
12#include <thread> 12#include <thread>
13#include <tuple> 13#include <tuple>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/param_package.h"
15#include "common/thread.h" 16#include "common/thread.h"
17#include "common/threadsafe_queue.h"
16#include "common/vector_math.h" 18#include "common/vector_math.h"
19#include "core/frontend/input.h"
20#include "input_common/motion_input.h"
17 21
18namespace InputCommon::CemuhookUDP { 22namespace InputCommon::CemuhookUDP {
19 23
@@ -28,9 +32,30 @@ struct PortInfo;
28struct Version; 32struct Version;
29} // namespace Response 33} // namespace Response
30 34
35enum class PadMotion {
36 GyroX,
37 GyroY,
38 GyroZ,
39 AccX,
40 AccY,
41 AccZ,
42 Undefined,
43};
44
45enum class PadTouch {
46 Click,
47 Undefined,
48};
49
50struct UDPPadStatus {
51 PadTouch touch{PadTouch::Undefined};
52 PadMotion motion{PadMotion::Undefined};
53 f32 motion_value{0.0f};
54};
55
31struct DeviceStatus { 56struct DeviceStatus {
32 std::mutex update_mutex; 57 std::mutex update_mutex;
33 std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status; 58 Input::MotionStatus motion_status;
34 std::tuple<float, float, bool> touch_status; 59 std::tuple<float, float, bool> touch_status;
35 60
36 // calibration data for scaling the device's touch area to 3ds 61 // calibration data for scaling the device's touch area to 3ds
@@ -45,22 +70,58 @@ struct DeviceStatus {
45 70
46class Client { 71class Client {
47public: 72public:
48 explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR, 73 // Initialize the UDP client capture and read sequence
49 u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); 74 Client();
75
76 // Close and release the client
50 ~Client(); 77 ~Client();
78
79 // Used for polling
80 void BeginConfiguration();
81 void EndConfiguration();
82
83 std::vector<Common::ParamPackage> GetInputDevices() const;
84
85 bool DeviceConnected(std::size_t pad) const;
86 void ReloadUDPClient();
51 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, 87 void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
52 u32 client_id = 24872); 88 u32 client_id = 24872);
53 89
90 std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue();
91 const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const;
92
93 DeviceStatus& GetPadState(std::size_t pad);
94 const DeviceStatus& GetPadState(std::size_t pad) const;
95
54private: 96private:
97 struct ClientData {
98 std::unique_ptr<Socket> socket;
99 DeviceStatus status;
100 std::thread thread;
101 u64 packet_sequence = 0;
102 u8 active;
103
104 // Realtime values
105 // motion is initalized with PID values for drift correction on joycons
106 InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
107 std::chrono::time_point<std::chrono::system_clock> last_motion_update;
108 };
109
110 // For shutting down, clear all data, join all threads, release usb
111 void Reset();
112
55 void OnVersion(Response::Version); 113 void OnVersion(Response::Version);
56 void OnPortInfo(Response::PortInfo); 114 void OnPortInfo(Response::PortInfo);
57 void OnPadData(Response::PadData); 115 void OnPadData(Response::PadData);
58 void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); 116 void StartCommunication(std::size_t client, const std::string& host, u16 port, u8 pad_index,
117 u32 client_id);
118 void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
119 const Common::Vec3<float>& gyro, bool touch);
120
121 bool configuring = false;
59 122
60 std::unique_ptr<Socket> socket; 123 std::array<ClientData, 4> clients;
61 std::shared_ptr<DeviceStatus> status; 124 std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;
62 std::thread thread;
63 u64 packet_sequence = 0;
64}; 125};
65 126
66/// An async job allowing configuration of the touchpad calibration. 127/// An async job allowing configuration of the touchpad calibration.
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 4b347e47e..eba077a36 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -1,105 +1,144 @@
1// Copyright 2018 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
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 <atomic>
6#include <list>
5#include <mutex> 7#include <mutex>
6#include <optional> 8#include <utility>
7#include <tuple> 9#include "common/assert.h"
8 10#include "common/threadsafe_queue.h"
9#include "common/param_package.h"
10#include "core/frontend/input.h"
11#include "core/settings.h"
12#include "input_common/udp/client.h" 11#include "input_common/udp/client.h"
13#include "input_common/udp/udp.h" 12#include "input_common/udp/udp.h"
14 13
15namespace InputCommon::CemuhookUDP { 14namespace InputCommon {
16 15
17class UDPTouchDevice final : public Input::TouchDevice { 16class UDPMotion final : public Input::MotionDevice {
18public: 17public:
19 explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 18 UDPMotion(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
20 std::tuple<float, float, bool> GetStatus() const override { 19 : ip(ip_), port(port_), pad(pad_), client(client_) {}
21 std::lock_guard guard(status->update_mutex); 20
22 return status->touch_status; 21 Input::MotionStatus GetStatus() const override {
22 return client->GetPadState(pad).motion_status;
23 } 23 }
24 24
25private: 25private:
26 std::shared_ptr<DeviceStatus> status; 26 const std::string ip;
27 const int port;
28 const int pad;
29 CemuhookUDP::Client* client;
30 mutable std::mutex mutex;
27}; 31};
28 32
29class UDPMotionDevice final : public Input::MotionDevice { 33/// A motion device factory that creates motion devices from JC Adapter
30public: 34UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
31 explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 35 : client(std::move(client_)) {}
32 std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { 36
33 std::lock_guard guard(status->update_mutex); 37/**
34 return status->motion_status; 38 * Creates motion device
35 } 39 * @param params contains parameters for creating the device:
40 * - "port": the nth jcpad on the adapter
41 */
42std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
43 const std::string ip = params.Get("ip", "127.0.0.1");
44 const int port = params.Get("port", 26760);
45 const int pad = params.Get("pad_index", 0);
46
47 return std::make_unique<UDPMotion>(ip, port, pad, client.get());
48}
36 49
37private: 50void UDPMotionFactory::BeginConfiguration() {
38 std::shared_ptr<DeviceStatus> status; 51 polling = true;
39}; 52 client->BeginConfiguration();
53}
40 54
41class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { 55void UDPMotionFactory::EndConfiguration() {
42public: 56 polling = false;
43 explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 57 client->EndConfiguration();
44 58}
45 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { 59
46 { 60Common::ParamPackage UDPMotionFactory::GetNextInput() {
47 std::lock_guard guard(status->update_mutex); 61 Common::ParamPackage params;
48 status->touch_calibration = DeviceStatus::CalibrationData{}; 62 CemuhookUDP::UDPPadStatus pad;
49 // These default values work well for DS4 but probably not other touch inputs 63 auto& queue = client->GetPadQueue();
50 status->touch_calibration->min_x = params.Get("min_x", 100); 64 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
51 status->touch_calibration->min_y = params.Get("min_y", 50); 65 while (queue[pad_number].Pop(pad)) {
52 status->touch_calibration->max_x = params.Get("max_x", 1800); 66 if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
53 status->touch_calibration->max_y = params.Get("max_y", 850); 67 continue;
68 }
69 params.Set("engine", "cemuhookudp");
70 params.Set("ip", "127.0.0.1");
71 params.Set("port", 26760);
72 params.Set("pad_index", static_cast<int>(pad_number));
73 params.Set("motion", static_cast<u16>(pad.motion));
74 return params;
54 } 75 }
55 return std::make_unique<UDPTouchDevice>(status);
56 } 76 }
77 return params;
78}
57 79
58private: 80class UDPTouch final : public Input::TouchDevice {
59 std::shared_ptr<DeviceStatus> status;
60};
61
62class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
63public: 81public:
64 explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} 82 UDPTouch(std::string ip_, int port_, int pad_, CemuhookUDP::Client* client_)
83 : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
65 84
66 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { 85 std::tuple<float, float, bool> GetStatus() const override {
67 return std::make_unique<UDPMotionDevice>(status); 86 return client->GetPadState(pad).touch_status;
68 } 87 }
69 88
70private: 89private:
71 std::shared_ptr<DeviceStatus> status; 90 const std::string ip;
91 const int port;
92 const int pad;
93 CemuhookUDP::Client* client;
94 mutable std::mutex mutex;
72}; 95};
73 96
74State::State() { 97/// A motion device factory that creates motion devices from JC Adapter
75 auto status = std::make_shared<DeviceStatus>(); 98UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
76 client = 99 : client(std::move(client_)) {}
77 std::make_unique<Client>(status, Settings::values.udp_input_address, 100
78 Settings::values.udp_input_port, Settings::values.udp_pad_index); 101/**
79 102 * Creates motion device
80 motion_factory = std::make_shared<UDPMotionFactory>(status); 103 * @param params contains parameters for creating the device:
81 touch_factory = std::make_shared<UDPTouchFactory>(status); 104 * - "port": the nth jcpad on the adapter
82 105 */
83 Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", motion_factory); 106std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
84 Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", touch_factory); 107 const std::string ip = params.Get("ip", "127.0.0.1");
108 const int port = params.Get("port", 26760);
109 const int pad = params.Get("pad_index", 0);
110
111 return std::make_unique<UDPTouch>(ip, port, pad, client.get());
85} 112}
86 113
87State::~State() { 114void UDPTouchFactory::BeginConfiguration() {
88 Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); 115 polling = true;
89 Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); 116 client->BeginConfiguration();
90} 117}
91 118
92std::vector<Common::ParamPackage> State::GetInputDevices() const { 119void UDPTouchFactory::EndConfiguration() {
93 // TODO support binding udp devices 120 polling = false;
94 return {}; 121 client->EndConfiguration();
95} 122}
96 123
97void State::ReloadUDPClient() { 124Common::ParamPackage UDPTouchFactory::GetNextInput() {
98 client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, 125 Common::ParamPackage params;
99 Settings::values.udp_pad_index); 126 CemuhookUDP::UDPPadStatus pad;
127 auto& queue = client->GetPadQueue();
128 for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
129 while (queue[pad_number].Pop(pad)) {
130 if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
131 continue;
132 }
133 params.Set("engine", "cemuhookudp");
134 params.Set("ip", "127.0.0.1");
135 params.Set("port", 26760);
136 params.Set("pad_index", static_cast<int>(pad_number));
137 params.Set("touch", static_cast<u16>(pad.touch));
138 return params;
139 }
140 }
141 return params;
100} 142}
101 143
102std::unique_ptr<State> Init() { 144} // namespace InputCommon
103 return std::make_unique<State>();
104}
105} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 672a5c812..ea3fd4175 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -1,32 +1,57 @@
1// Copyright 2018 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
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#pragma once 5#pragma once
6 6
7#include <memory> 7#include <memory>
8#include <vector> 8#include "core/frontend/input.h"
9#include "common/param_package.h" 9#include "input_common/udp/client.h"
10 10
11namespace InputCommon::CemuhookUDP { 11namespace InputCommon {
12 12
13class Client; 13/// A motion device factory that creates motion devices from udp clients
14class UDPMotionFactory; 14class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
15class UDPTouchFactory;
16
17class State {
18public: 15public:
19 State(); 16 explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
20 ~State(); 17
21 void ReloadUDPClient(); 18 std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
22 std::vector<Common::ParamPackage> GetInputDevices() const; 19
20 Common::ParamPackage GetNextInput();
21
22 /// For device input configuration/polling
23 void BeginConfiguration();
24 void EndConfiguration();
25
26 bool IsPolling() const {
27 return polling;
28 }
23 29
24private: 30private:
25 std::unique_ptr<Client> client; 31 std::shared_ptr<CemuhookUDP::Client> client;
26 std::shared_ptr<UDPMotionFactory> motion_factory; 32 bool polling = false;
27 std::shared_ptr<UDPTouchFactory> touch_factory;
28}; 33};
29 34
30std::unique_ptr<State> Init(); 35/// A touch device factory that creates touch devices from udp clients
36class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
37public:
38 explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
39
40 std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
41
42 Common::ParamPackage GetNextInput();
43
44 /// For device input configuration/polling
45 void BeginConfiguration();
46 void EndConfiguration();
47
48 bool IsPolling() const {
49 return polling;
50 }
51
52private:
53 std::shared_ptr<CemuhookUDP::Client> client;
54 bool polling = false;
55};
31 56
32} // namespace InputCommon::CemuhookUDP 57} // namespace InputCommon
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index d85f1e9d1..f52b55ef3 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -269,5 +269,5 @@ endif()
269if (MSVC) 269if (MSVC)
270 target_compile_options(video_core PRIVATE /we4267) 270 target_compile_options(video_core PRIVATE /we4267)
271else() 271else()
272 target_compile_options(video_core PRIVATE -Werror=conversion -Wno-error=sign-conversion) 272 target_compile_options(video_core PRIVATE -Werror=conversion -Wno-error=sign-conversion -Werror=switch)
273endif() 273endif()
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 6e50661a3..9409c4075 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -87,12 +87,12 @@ void Fermi2D::HandleSurfaceCopy() {
87 const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2}; 87 const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
88 const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2, 88 const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2,
89 dst_blit_y2}; 89 dst_blit_y2};
90 Config copy_config; 90 const Config copy_config{
91 copy_config.operation = regs.operation; 91 .operation = regs.operation,
92 copy_config.filter = regs.blit_control.filter; 92 .filter = regs.blit_control.filter,
93 copy_config.src_rect = src_rect; 93 .src_rect = src_rect,
94 copy_config.dst_rect = dst_rect; 94 .dst_rect = dst_rect,
95 95 };
96 if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) { 96 if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
97 UNIMPLEMENTED(); 97 UNIMPLEMENTED();
98 } 98 }
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 213abfaae..0909709ec 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -145,8 +145,8 @@ public:
145 } regs{}; 145 } regs{};
146 146
147 struct Config { 147 struct Config {
148 Operation operation; 148 Operation operation{};
149 Filter filter; 149 Filter filter{};
150 Common::Rectangle<u32> src_rect; 150 Common::Rectangle<u32> src_rect;
151 Common::Rectangle<u32> dst_rect; 151 Common::Rectangle<u32> dst_rect;
152 }; 152 };
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 3f75fcd2b..ce3a65122 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1443,8 +1443,10 @@ private:
1443 return expr + ", vec2(0.0), vec2(0.0))"; 1443 return expr + ", vec2(0.0), vec2(0.0))";
1444 case TextureType::TextureCube: 1444 case TextureType::TextureCube:
1445 return expr + ", vec3(0.0), vec3(0.0))"; 1445 return expr + ", vec3(0.0), vec3(0.0))";
1446 default:
1447 UNREACHABLE();
1448 break;
1446 } 1449 }
1447 UNREACHABLE();
1448 } 1450 }
1449 1451
1450 for (const auto& variant : extras) { 1452 for (const auto& variant : extras) {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index fe9bd4b5a..a8be2aa37 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -47,6 +47,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
47 return GL_UNSIGNED_INT; 47 return GL_UNSIGNED_INT;
48 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 48 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
49 return GL_UNSIGNED_INT_2_10_10_10_REV; 49 return GL_UNSIGNED_INT_2_10_10_10_REV;
50 default:
51 break;
50 } 52 }
51 break; 53 break;
52 case Maxwell::VertexAttribute::Type::SignedNorm: 54 case Maxwell::VertexAttribute::Type::SignedNorm:
@@ -70,6 +72,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
70 return GL_INT; 72 return GL_INT;
71 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 73 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
72 return GL_INT_2_10_10_10_REV; 74 return GL_INT_2_10_10_10_REV;
75 default:
76 break;
73 } 77 }
74 break; 78 break;
75 case Maxwell::VertexAttribute::Type::Float: 79 case Maxwell::VertexAttribute::Type::Float:
@@ -84,6 +88,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
84 case Maxwell::VertexAttribute::Size::Size_32_32_32: 88 case Maxwell::VertexAttribute::Size::Size_32_32_32:
85 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 89 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
86 return GL_FLOAT; 90 return GL_FLOAT;
91 default:
92 break;
87 } 93 }
88 break; 94 break;
89 } 95 }
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index f8c77f4fa..d22de1d81 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -78,9 +78,10 @@ VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode w
78 case Tegra::Texture::WrapMode::MirrorOnceBorder: 78 case Tegra::Texture::WrapMode::MirrorOnceBorder:
79 UNIMPLEMENTED(); 79 UNIMPLEMENTED();
80 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; 80 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
81 default:
82 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
83 return {};
81 } 84 }
82 UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
83 return {};
84} 85}
85 86
86VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { 87VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) {
@@ -298,9 +299,10 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
298 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 299 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
299 case Maxwell::PrimitiveTopology::Patches: 300 case Maxwell::PrimitiveTopology::Patches:
300 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; 301 return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
302 default:
303 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
304 return {};
301 } 305 }
302 UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
303 return {};
304} 306}
305 307
306VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { 308VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
@@ -325,6 +327,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
325 return VK_FORMAT_R16G16B16A16_UNORM; 327 return VK_FORMAT_R16G16B16A16_UNORM;
326 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 328 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
327 return VK_FORMAT_A2B10G10R10_UNORM_PACK32; 329 return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
330 default:
331 break;
328 } 332 }
329 break; 333 break;
330 case Maxwell::VertexAttribute::Type::SignedNorm: 334 case Maxwell::VertexAttribute::Type::SignedNorm:
@@ -347,6 +351,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
347 return VK_FORMAT_R16G16B16A16_SNORM; 351 return VK_FORMAT_R16G16B16A16_SNORM;
348 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 352 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
349 return VK_FORMAT_A2B10G10R10_SNORM_PACK32; 353 return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
354 default:
355 break;
350 } 356 }
351 break; 357 break;
352 case Maxwell::VertexAttribute::Type::UnsignedScaled: 358 case Maxwell::VertexAttribute::Type::UnsignedScaled:
@@ -369,6 +375,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
369 return VK_FORMAT_R16G16B16A16_USCALED; 375 return VK_FORMAT_R16G16B16A16_USCALED;
370 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 376 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
371 return VK_FORMAT_A2B10G10R10_USCALED_PACK32; 377 return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
378 default:
379 break;
372 } 380 }
373 break; 381 break;
374 case Maxwell::VertexAttribute::Type::SignedScaled: 382 case Maxwell::VertexAttribute::Type::SignedScaled:
@@ -391,6 +399,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
391 return VK_FORMAT_R16G16B16A16_SSCALED; 399 return VK_FORMAT_R16G16B16A16_SSCALED;
392 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 400 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
393 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32; 401 return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
402 default:
403 break;
394 } 404 }
395 break; 405 break;
396 case Maxwell::VertexAttribute::Type::UnsignedInt: 406 case Maxwell::VertexAttribute::Type::UnsignedInt:
@@ -421,6 +431,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
421 return VK_FORMAT_R32G32B32A32_UINT; 431 return VK_FORMAT_R32G32B32A32_UINT;
422 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 432 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
423 return VK_FORMAT_A2B10G10R10_UINT_PACK32; 433 return VK_FORMAT_A2B10G10R10_UINT_PACK32;
434 default:
435 break;
424 } 436 }
425 break; 437 break;
426 case Maxwell::VertexAttribute::Type::SignedInt: 438 case Maxwell::VertexAttribute::Type::SignedInt:
@@ -451,6 +463,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
451 return VK_FORMAT_R32G32B32A32_SINT; 463 return VK_FORMAT_R32G32B32A32_SINT;
452 case Maxwell::VertexAttribute::Size::Size_10_10_10_2: 464 case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
453 return VK_FORMAT_A2B10G10R10_SINT_PACK32; 465 return VK_FORMAT_A2B10G10R10_SINT_PACK32;
466 default:
467 break;
454 } 468 }
455 break; 469 break;
456 case Maxwell::VertexAttribute::Type::Float: 470 case Maxwell::VertexAttribute::Type::Float:
@@ -471,6 +485,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
471 return VK_FORMAT_R32G32B32_SFLOAT; 485 return VK_FORMAT_R32G32B32_SFLOAT;
472 case Maxwell::VertexAttribute::Size::Size_32_32_32_32: 486 case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
473 return VK_FORMAT_R32G32B32A32_SFLOAT; 487 return VK_FORMAT_R32G32B32A32_SFLOAT;
488 default:
489 break;
474 } 490 }
475 break; 491 break;
476 } 492 }
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 013865aa4..fe291a148 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -262,6 +262,22 @@ const char* ToString(VkResult result) noexcept {
262 return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT"; 262 return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT";
263 case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: 263 case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
264 return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; 264 return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
265 case VkResult::VK_ERROR_UNKNOWN:
266 return "VK_ERROR_UNKNOWN";
267 case VkResult::VK_ERROR_INCOMPATIBLE_VERSION_KHR:
268 return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
269 case VkResult::VK_THREAD_IDLE_KHR:
270 return "VK_THREAD_IDLE_KHR";
271 case VkResult::VK_THREAD_DONE_KHR:
272 return "VK_THREAD_DONE_KHR";
273 case VkResult::VK_OPERATION_DEFERRED_KHR:
274 return "VK_OPERATION_DEFERRED_KHR";
275 case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
276 return "VK_OPERATION_NOT_DEFERRED_KHR";
277 case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
278 return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
279 case VkResult::VK_RESULT_MAX_ENUM:
280 return "VK_RESULT_MAX_ENUM";
265 } 281 }
266 return "Unknown"; 282 return "Unknown";
267} 283}
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index a276aee44..88103fede 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -53,6 +53,9 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
53 absolute_a = ((instr.value >> 44) & 1) != 0; 53 absolute_a = ((instr.value >> 44) & 1) != 0;
54 absolute_b = ((instr.value >> 54) & 1) != 0; 54 absolute_b = ((instr.value >> 54) & 1) != 0;
55 break; 55 break;
56 default:
57 UNREACHABLE();
58 break;
56 } 59 }
57 60
58 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a); 61 Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index e75ca4fdb..618d309d2 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -119,6 +119,8 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor,
119 return descriptor.r_type; 119 return descriptor.r_type;
120 } 120 }
121 break; 121 break;
122 default:
123 break;
122 } 124 }
123 UNIMPLEMENTED_MSG("Texture format not implemented={}", format); 125 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
124 return ComponentType::FLOAT; 126 return ComponentType::FLOAT;
@@ -220,9 +222,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
220 return (component == 0 || component == 1) ? 8 : 0; 222 return (component == 0 || component == 1) ? 8 : 0;
221 case TextureFormat::G4R4: 223 case TextureFormat::G4R4:
222 return (component == 0 || component == 1) ? 4 : 0; 224 return (component == 0 || component == 1) ? 4 : 0;
225 default:
226 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
227 return 0;
223 } 228 }
224 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
225 return 0;
226} 229}
227 230
228std::size_t GetImageComponentMask(TextureFormat format) { 231std::size_t GetImageComponentMask(TextureFormat format) {
@@ -257,9 +260,10 @@ std::size_t GetImageComponentMask(TextureFormat format) {
257 case TextureFormat::R8: 260 case TextureFormat::R8:
258 case TextureFormat::R1: 261 case TextureFormat::R1:
259 return std::size_t{R}; 262 return std::size_t{R};
263 default:
264 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
265 return std::size_t{R | G | B | A};
260 } 266 }
261 UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
262 return std::size_t{R | G | B | A};
263} 267}
264 268
265std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { 269std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) {
@@ -463,7 +467,10 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
463 return OperationCode::AtomicImageXor; 467 return OperationCode::AtomicImageXor;
464 case Tegra::Shader::ImageAtomicOperation::Exch: 468 case Tegra::Shader::ImageAtomicOperation::Exch:
465 return OperationCode::AtomicImageExchange; 469 return OperationCode::AtomicImageExchange;
470 default:
471 break;
466 } 472 }
473 break;
467 default: 474 default:
468 break; 475 break;
469 } 476 }
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 29ebf65ba..a03b50e39 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -763,7 +763,7 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
763 763
764Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { 764Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
765 const auto texture_type{instr.tld.texture_type}; 765 const auto texture_type{instr.tld.texture_type};
766 const bool is_array{instr.tld.is_array}; 766 const bool is_array{instr.tld.is_array != 0};
767 const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL}; 767 const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL};
768 const std::size_t coord_count{GetCoordCount(texture_type)}; 768 const std::size_t coord_count{GetCoordCount(texture_type)};
769 769
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 2bc55a26a..d2913d613 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -36,6 +36,11 @@ const std::array<int, Settings::NativeButton::NumButtons> Config::default_button
36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V, 36 Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
37}; 37};
38 38
39const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
40 Qt::Key_7,
41 Qt::Key_8,
42};
43
39const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ 44const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
40 { 45 {
41 Qt::Key_Up, 46 Qt::Key_Up,
@@ -284,6 +289,22 @@ void Config::ReadPlayerValues() {
284 } 289 }
285 } 290 }
286 291
292 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
293 const std::string default_param =
294 InputCommon::GenerateKeyboardParam(default_motions[i]);
295 auto& player_motions = player.motions[i];
296
297 player_motions = qt_config
298 ->value(QStringLiteral("player_%1_").arg(p) +
299 QString::fromUtf8(Settings::NativeMotion::mapping[i]),
300 QString::fromStdString(default_param))
301 .toString()
302 .toStdString();
303 if (player_motions.empty()) {
304 player_motions = default_param;
305 }
306 }
307
287 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 308 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
288 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 309 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
289 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 310 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -424,6 +445,7 @@ void Config::ReadControlValues() {
424 445
425 Settings::values.vibration_enabled = 446 Settings::values.vibration_enabled =
426 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool(); 447 ReadSetting(QStringLiteral("vibration_enabled"), true).toBool();
448 Settings::values.motion_enabled = ReadSetting(QStringLiteral("motion_enabled"), true).toBool();
427 Settings::values.use_docked_mode = 449 Settings::values.use_docked_mode =
428 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); 450 ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
429 451
@@ -922,6 +944,14 @@ void Config::SavePlayerValues() {
922 QString::fromStdString(player.buttons[i]), 944 QString::fromStdString(player.buttons[i]),
923 QString::fromStdString(default_param)); 945 QString::fromStdString(default_param));
924 } 946 }
947 for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
948 const std::string default_param =
949 InputCommon::GenerateKeyboardParam(default_motions[i]);
950 WriteSetting(QStringLiteral("player_%1_").arg(p) +
951 QString::fromStdString(Settings::NativeMotion::mapping[i]),
952 QString::fromStdString(player.motions[i]),
953 QString::fromStdString(default_param));
954 }
925 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { 955 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
926 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 956 const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
927 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], 957 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
@@ -1062,6 +1092,7 @@ void Config::SaveControlValues() {
1062 SaveMotionTouchValues(); 1092 SaveMotionTouchValues();
1063 1093
1064 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true); 1094 WriteSetting(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled, true);
1095 WriteSetting(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
1065 WriteSetting(QStringLiteral("motion_device"), 1096 WriteSetting(QStringLiteral("motion_device"),
1066 QString::fromStdString(Settings::values.motion_device), 1097 QString::fromStdString(Settings::values.motion_device),
1067 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); 1098 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index ca0d29c6c..5d8e45d78 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -23,6 +23,7 @@ public:
23 void Save(); 23 void Save();
24 24
25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; 25 static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
26 static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
26 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs; 27 static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
27 static const std::array<int, 2> default_stick_mod; 28 static const std::array<int, 2> default_stick_mod;
28 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons> 29 static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7ea17a4db..2725fcb2b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -146,6 +146,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
146 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem); 146 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
147 }); 147 });
148 148
149 connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
150 CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
151 });
152
149 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); 153 connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
150 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); }); 154 connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
151 155
@@ -172,6 +176,7 @@ void ConfigureInput::ApplyConfiguration() {
172 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode); 176 OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
173 177
174 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked(); 178 Settings::values.vibration_enabled = ui->vibrationGroup->isChecked();
179 Settings::values.motion_enabled = ui->motionGroup->isChecked();
175} 180}
176 181
177void ConfigureInput::changeEvent(QEvent* event) { 182void ConfigureInput::changeEvent(QEvent* event) {
@@ -191,6 +196,7 @@ void ConfigureInput::LoadConfiguration() {
191 UpdateDockedState(Settings::values.players[8].connected); 196 UpdateDockedState(Settings::values.players[8].connected);
192 197
193 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled); 198 ui->vibrationGroup->setChecked(Settings::values.vibration_enabled);
199 ui->motionGroup->setChecked(Settings::values.motion_enabled);
194} 200}
195 201
196void ConfigureInput::LoadPlayerControllerIndices() { 202void ConfigureInput::LoadPlayerControllerIndices() {
@@ -217,6 +223,7 @@ void ConfigureInput::RestoreDefaults() {
217 ui->radioDocked->setChecked(true); 223 ui->radioDocked->setChecked(true);
218 ui->radioUndocked->setChecked(false); 224 ui->radioUndocked->setChecked(false);
219 ui->vibrationGroup->setChecked(true); 225 ui->vibrationGroup->setChecked(true);
226 ui->motionGroup->setChecked(true);
220} 227}
221 228
222void ConfigureInput::UpdateDockedState(bool is_handheld) { 229void ConfigureInput::UpdateDockedState(bool is_handheld) {
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index a53578837..698cb1940 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -18,6 +18,7 @@
18#include "core/hle/service/sm/sm.h" 18#include "core/hle/service/sm/sm.h"
19#include "input_common/gcadapter/gc_poller.h" 19#include "input_common/gcadapter/gc_poller.h"
20#include "input_common/main.h" 20#include "input_common/main.h"
21#include "input_common/udp/udp.h"
21#include "ui_configure_input_player.h" 22#include "ui_configure_input_player.h"
22#include "yuzu/configuration/config.h" 23#include "yuzu/configuration/config.h"
23#include "yuzu/configuration/configure_input_player.h" 24#include "yuzu/configuration/configure_input_player.h"
@@ -149,6 +150,14 @@ QString ButtonToText(const Common::ParamPackage& param) {
149 return GetKeyName(param.Get("code", 0)); 150 return GetKeyName(param.Get("code", 0));
150 } 151 }
151 152
153 if (param.Get("engine", "") == "cemuhookudp") {
154 if (param.Has("pad_index")) {
155 const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
156 return QObject::tr("Motion %1").arg(motion_str);
157 }
158 return GetKeyName(param.Get("code", 0));
159 }
160
152 if (param.Get("engine", "") == "sdl") { 161 if (param.Get("engine", "") == "sdl") {
153 if (param.Has("hat")) { 162 if (param.Has("hat")) {
154 const QString hat_str = QString::fromStdString(param.Get("hat", "")); 163 const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -247,6 +256,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
247 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, 256 ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
248 }; 257 };
249 258
259 mod_buttons = {
260 ui->buttonLStickMod,
261 ui->buttonRStickMod,
262 };
263
250 analog_map_buttons = {{ 264 analog_map_buttons = {{
251 { 265 {
252 ui->buttonLStickUp, 266 ui->buttonLStickUp,
@@ -262,6 +276,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
262 }, 276 },
263 }}; 277 }};
264 278
279 motion_map = {
280 ui->buttonMotionLeft,
281 ui->buttonMotionRight,
282 };
283
265 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; 284 analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
266 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; 285 analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
267 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup}; 286 analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
@@ -271,7 +290,7 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
271 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange}; 290 analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
272 291
273 const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param, 292 const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
274 int default_val) { 293 int default_val, InputCommon::Polling::DeviceType type) {
275 connect(button, &QPushButton::clicked, [=, this] { 294 connect(button, &QPushButton::clicked, [=, this] {
276 HandleClick( 295 HandleClick(
277 button, 296 button,
@@ -291,22 +310,56 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
291 } 310 }
292 *param = std::move(params); 311 *param = std::move(params);
293 }, 312 },
294 InputCommon::Polling::DeviceType::Button); 313 type);
295 }); 314 });
296 }; 315 };
297 316
298 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 317 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
299 auto* const button = button_map[button_id]; 318 auto* const button = button_map[button_id];
319
300 if (button == nullptr) { 320 if (button == nullptr) {
301 continue; 321 continue;
302 } 322 }
323
303 ConfigureButtonClick(button_map[button_id], &buttons_param[button_id], 324 ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
304 Config::default_buttons[button_id]); 325 Config::default_buttons[button_id],
326 InputCommon::Polling::DeviceType::Button);
327
328 button->setContextMenuPolicy(Qt::CustomContextMenu);
329
330 connect(button, &QPushButton::customContextMenuRequested,
331 [=, this](const QPoint& menu_location) {
332 QMenu context_menu;
333 context_menu.addAction(tr("Clear"), [&] {
334 buttons_param[button_id].Clear();
335 button_map[button_id]->setText(tr("[not set]"));
336 });
337 context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
338 });
305 } 339 }
306 340
307 // Handle clicks for the modifier buttons as well. 341 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
308 ConfigureButtonClick(ui->buttonLStickMod, &lstick_mod, Config::default_stick_mod[0]); 342 auto* const button = motion_map[motion_id];
309 ConfigureButtonClick(ui->buttonRStickMod, &rstick_mod, Config::default_stick_mod[1]); 343 if (button == nullptr) {
344 continue;
345 }
346
347 ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id],
348 Config::default_motions[motion_id],
349 InputCommon::Polling::DeviceType::Motion);
350
351 button->setContextMenuPolicy(Qt::CustomContextMenu);
352
353 connect(button, &QPushButton::customContextMenuRequested,
354 [=, this](const QPoint& menu_location) {
355 QMenu context_menu;
356 context_menu.addAction(tr("Clear"), [&] {
357 motions_param[motion_id].Clear();
358 motion_map[motion_id]->setText(tr("[not set]"));
359 });
360 context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
361 });
362 }
310 363
311 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 364 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
312 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 365 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
@@ -325,8 +378,38 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
325 }, 378 },
326 InputCommon::Polling::DeviceType::AnalogPreferred); 379 InputCommon::Polling::DeviceType::AnalogPreferred);
327 }); 380 });
381
382 analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
383
384 connect(analog_button, &QPushButton::customContextMenuRequested,
385 [=, this](const QPoint& menu_location) {
386 QMenu context_menu;
387 context_menu.addAction(tr("Clear"), [&] {
388 analogs_param[analog_id].Clear();
389 analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
390 });
391 context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
392 menu_location));
393 });
328 } 394 }
329 395
396 // Handle clicks for the modifier buttons as well.
397 ConfigureButtonClick(mod_buttons[analog_id], &stick_mod_param[analog_id],
398 Config::default_stick_mod[analog_id],
399 InputCommon::Polling::DeviceType::Button);
400
401 mod_buttons[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu);
402
403 connect(mod_buttons[analog_id], &QPushButton::customContextMenuRequested,
404 [=, this](const QPoint& menu_location) {
405 QMenu context_menu;
406 context_menu.addAction(tr("Clear"), [&] {
407 stick_mod_param[analog_id].Clear();
408 mod_buttons[analog_id]->setText(tr("[not set]"));
409 });
410 context_menu.exec(mod_buttons[analog_id]->mapToGlobal(menu_location));
411 });
412
330 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged), 413 connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
331 [=, this] { 414 [=, this] {
332 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value(); 415 const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
@@ -385,9 +468,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
385 468
386 UpdateControllerIcon(); 469 UpdateControllerIcon();
387 UpdateControllerAvailableButtons(); 470 UpdateControllerAvailableButtons();
471 UpdateMotionButtons();
388 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) { 472 connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
389 UpdateControllerIcon(); 473 UpdateControllerIcon();
390 UpdateControllerAvailableButtons(); 474 UpdateControllerAvailableButtons();
475 UpdateMotionButtons();
391 }); 476 });
392 477
393 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this, 478 connect(ui->comboDevices, qOverload<int>(&QComboBox::currentIndexChanged), this,
@@ -417,6 +502,13 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
417 return; 502 return;
418 } 503 }
419 } 504 }
505 if (input_subsystem->GetUDPMotions()->IsPolling()) {
506 params = input_subsystem->GetUDPMotions()->GetNextInput();
507 if (params.Has("engine")) {
508 SetPollingResult(params, false);
509 return;
510 }
511 }
420 for (auto& poller : device_pollers) { 512 for (auto& poller : device_pollers) {
421 params = poller->GetNextInput(); 513 params = poller->GetNextInput();
422 if (params.Has("engine")) { 514 if (params.Has("engine")) {
@@ -448,6 +540,10 @@ void ConfigureInputPlayer::ApplyConfiguration() {
448 return; 540 return;
449 } 541 }
450 542
543 auto& motions = player.motions;
544 std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
545 [](const Common::ParamPackage& param) { return param.Serialize(); });
546
451 player.controller_type = 547 player.controller_type =
452 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex()); 548 static_cast<Settings::ControllerType>(ui->comboControllerType->currentIndex());
453 player.connected = ui->groupConnectedController->isChecked(); 549 player.connected = ui->groupConnectedController->isChecked();
@@ -501,6 +597,8 @@ void ConfigureInputPlayer::LoadConfiguration() {
501 [](const std::string& str) { return Common::ParamPackage(str); }); 597 [](const std::string& str) { return Common::ParamPackage(str); });
502 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(), 598 std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
503 [](const std::string& str) { return Common::ParamPackage(str); }); 599 [](const std::string& str) { return Common::ParamPackage(str); });
600 std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
601 [](const std::string& str) { return Common::ParamPackage(str); });
504 } 602 }
505 603
506 UpdateUI(); 604 UpdateUI();
@@ -530,20 +628,23 @@ void ConfigureInputPlayer::RestoreDefaults() {
530 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; 628 InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
531 } 629 }
532 630
533 // Reset Modifier Buttons 631 // Reset Analogs and Modifier Buttons
534 lstick_mod =
535 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[0]));
536 rstick_mod =
537 Common::ParamPackage(InputCommon::GenerateKeyboardParam(Config::default_stick_mod[1]));
538
539 // Reset Analogs
540 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 632 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
541 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 633 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
542 Common::ParamPackage params{InputCommon::GenerateKeyboardParam( 634 Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
543 Config::default_analogs[analog_id][sub_button_id])}; 635 Config::default_analogs[analog_id][sub_button_id])};
544 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); 636 SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
545 } 637 }
638
639 stick_mod_param[analog_id] = Common::ParamPackage(
640 InputCommon::GenerateKeyboardParam(Config::default_stick_mod[analog_id]));
641 }
642
643 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
644 motions_param[motion_id] = Common::ParamPackage{
645 InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
546 } 646 }
647
547 UpdateUI(); 648 UpdateUI();
548 UpdateInputDevices(); 649 UpdateInputDevices();
549 ui->comboControllerType->setCurrentIndex(0); 650 ui->comboControllerType->setCurrentIndex(0);
@@ -552,25 +653,33 @@ void ConfigureInputPlayer::RestoreDefaults() {
552void ConfigureInputPlayer::ClearAll() { 653void ConfigureInputPlayer::ClearAll() {
553 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { 654 for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
554 const auto* const button = button_map[button_id]; 655 const auto* const button = button_map[button_id];
555 if (button == nullptr || !button->isEnabled()) { 656 if (button == nullptr) {
556 continue; 657 continue;
557 } 658 }
558 659
559 buttons_param[button_id].Clear(); 660 buttons_param[button_id].Clear();
560 } 661 }
561 662
562 lstick_mod.Clear();
563 rstick_mod.Clear();
564
565 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 663 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
566 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 664 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
567 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id]; 665 const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
568 if (analog_button == nullptr || !analog_button->isEnabled()) { 666 if (analog_button == nullptr) {
569 continue; 667 continue;
570 } 668 }
571 669
572 analogs_param[analog_id].Clear(); 670 analogs_param[analog_id].Clear();
573 } 671 }
672
673 stick_mod_param[analog_id].Clear();
674 }
675
676 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
677 const auto* const motion_button = motion_map[motion_id];
678 if (motion_button == nullptr) {
679 continue;
680 }
681
682 motions_param[motion_id].Clear();
574 } 683 }
575 684
576 UpdateUI(); 685 UpdateUI();
@@ -582,8 +691,9 @@ void ConfigureInputPlayer::UpdateUI() {
582 button_map[button]->setText(ButtonToText(buttons_param[button])); 691 button_map[button]->setText(ButtonToText(buttons_param[button]));
583 } 692 }
584 693
585 ui->buttonLStickMod->setText(ButtonToText(lstick_mod)); 694 for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
586 ui->buttonRStickMod->setText(ButtonToText(rstick_mod)); 695 motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
696 }
587 697
588 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { 698 for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
589 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { 699 for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
@@ -597,6 +707,8 @@ void ConfigureInputPlayer::UpdateUI() {
597 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); 707 AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
598 } 708 }
599 709
710 mod_buttons[analog_id]->setText(ButtonToText(stick_mod_param[analog_id]));
711
600 const auto deadzone_label = analog_map_deadzone_label[analog_id]; 712 const auto deadzone_label = analog_map_deadzone_label[analog_id];
601 const auto deadzone_slider = analog_map_deadzone_slider[analog_id]; 713 const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
602 const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id]; 714 const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
@@ -659,7 +771,11 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() {
659void ConfigureInputPlayer::HandleClick( 771void ConfigureInputPlayer::HandleClick(
660 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter, 772 QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
661 InputCommon::Polling::DeviceType type) { 773 InputCommon::Polling::DeviceType type) {
662 button->setText(tr("[waiting]")); 774 if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
775 button->setText(tr("Shake!"));
776 } else {
777 button->setText(tr("[waiting]"));
778 }
663 button->setFocus(); 779 button->setFocus();
664 780
665 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a 781 // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
@@ -683,6 +799,10 @@ void ConfigureInputPlayer::HandleClick(
683 input_subsystem->GetGCAnalogs()->BeginConfiguration(); 799 input_subsystem->GetGCAnalogs()->BeginConfiguration();
684 } 800 }
685 801
802 if (type == InputCommon::Polling::DeviceType::Motion) {
803 input_subsystem->GetUDPMotions()->BeginConfiguration();
804 }
805
686 timeout_timer->start(2500); // Cancel after 2.5 seconds 806 timeout_timer->start(2500); // Cancel after 2.5 seconds
687 poll_timer->start(50); // Check for new inputs every 50ms 807 poll_timer->start(50); // Check for new inputs every 50ms
688} 808}
@@ -700,6 +820,8 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
700 input_subsystem->GetGCButtons()->EndConfiguration(); 820 input_subsystem->GetGCButtons()->EndConfiguration();
701 input_subsystem->GetGCAnalogs()->EndConfiguration(); 821 input_subsystem->GetGCAnalogs()->EndConfiguration();
702 822
823 input_subsystem->GetUDPMotions()->EndConfiguration();
824
703 if (!abort) { 825 if (!abort) {
704 (*input_setter)(params); 826 (*input_setter)(params);
705 } 827 }
@@ -832,6 +954,37 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
832 } 954 }
833} 955}
834 956
957void ConfigureInputPlayer::UpdateMotionButtons() {
958 if (debug) {
959 // Motion isn't used with the debug controller, hide both groupboxes.
960 ui->buttonMotionLeftGroup->hide();
961 ui->buttonMotionRightGroup->hide();
962 return;
963 }
964
965 // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
966 switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
967 case Settings::ControllerType::ProController:
968 case Settings::ControllerType::LeftJoycon:
969 case Settings::ControllerType::Handheld:
970 // Show "Motion 1" and hide "Motion 2".
971 ui->buttonMotionLeftGroup->show();
972 ui->buttonMotionRightGroup->hide();
973 break;
974 case Settings::ControllerType::RightJoycon:
975 // Show "Motion 2" and hide "Motion 1".
976 ui->buttonMotionLeftGroup->hide();
977 ui->buttonMotionRightGroup->show();
978 break;
979 case Settings::ControllerType::DualJoyconDetached:
980 default:
981 // Show both "Motion 1/2".
982 ui->buttonMotionLeftGroup->show();
983 ui->buttonMotionRightGroup->show();
984 break;
985 }
986}
987
835void ConfigureInputPlayer::showEvent(QShowEvent* event) { 988void ConfigureInputPlayer::showEvent(QShowEvent* event) {
836 if (bottom_row == nullptr) { 989 if (bottom_row == nullptr) {
837 return; 990 return;
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index a25bc3bd9..ce443dec5 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -107,6 +107,9 @@ private:
107 /// Hides and disables controller settings based on the current controller type. 107 /// Hides and disables controller settings based on the current controller type.
108 void UpdateControllerAvailableButtons(); 108 void UpdateControllerAvailableButtons();
109 109
110 /// Shows or hides motion groupboxes based on the current controller type.
111 void UpdateMotionButtons();
112
110 /// Gets the default controller mapping for this device and auto configures the input to match. 113 /// Gets the default controller mapping for this device and auto configures the input to match.
111 void UpdateMappingWithDefaults(); 114 void UpdateMappingWithDefaults();
112 115
@@ -128,14 +131,17 @@ private:
128 131
129 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param; 132 std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
130 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param; 133 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
134 std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> stick_mod_param;
135 std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
131 136
132 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4; 137 static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
133 138
134 /// Each button input is represented by a QPushButton. 139 /// Each button input is represented by a QPushButton.
135 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map; 140 std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
141 /// Each motion input is represented by a QPushButton.
142 std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
136 /// Extra buttons for the modifiers. 143 /// Extra buttons for the modifiers.
137 Common::ParamPackage lstick_mod; 144 std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> mod_buttons;
138 Common::ParamPackage rstick_mod;
139 145
140 /// A group of four QPushButtons represent one analog input. The buttons each represent up, 146 /// A group of four QPushButtons represent one analog input. The buttons each represent up,
141 /// down, left, right, respectively. 147 /// down, left, right, respectively.
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index 9bc681894..e03461d9d 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1983,6 +1983,9 @@
1983 <property name="spacing"> 1983 <property name="spacing">
1984 <number>3</number> 1984 <number>3</number>
1985 </property> 1985 </property>
1986 <property name="topMargin">
1987 <number>0</number>
1988 </property>
1986 <item> 1989 <item>
1987 <spacer name="horizontalSpacerMiscButtons1"> 1990 <spacer name="horizontalSpacerMiscButtons1">
1988 <property name="orientation"> 1991 <property name="orientation">
@@ -1990,21 +1993,119 @@
1990 </property> 1993 </property>
1991 <property name="sizeHint" stdset="0"> 1994 <property name="sizeHint" stdset="0">
1992 <size> 1995 <size>
1993 <width>40</width> 1996 <width>20</width>
1994 <height>0</height> 1997 <height>20</height>
1995 </size> 1998 </size>
1996 </property> 1999 </property>
1997 </spacer> 2000 </spacer>
1998 </item> 2001 </item>
1999 <item> 2002 <item>
2003 <widget class="QGroupBox" name="buttonMotionLeftGroup">
2004 <property name="title">
2005 <string>Motion 1</string>
2006 </property>
2007 <property name="alignment">
2008 <set>Qt::AlignCenter</set>
2009 </property>
2010 <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
2011 <property name="spacing">
2012 <number>3</number>
2013 </property>
2014 <property name="leftMargin">
2015 <number>3</number>
2016 </property>
2017 <property name="topMargin">
2018 <number>3</number>
2019 </property>
2020 <property name="rightMargin">
2021 <number>3</number>
2022 </property>
2023 <property name="bottomMargin">
2024 <number>3</number>
2025 </property>
2026 <item>
2027 <widget class="QPushButton" name="buttonMotionLeft">
2028 <property name="minimumSize">
2029 <size>
2030 <width>57</width>
2031 <height>0</height>
2032 </size>
2033 </property>
2034 <property name="maximumSize">
2035 <size>
2036 <width>55</width>
2037 <height>16777215</height>
2038 </size>
2039 </property>
2040 <property name="styleSheet">
2041 <string notr="true">min-width: 55px;</string>
2042 </property>
2043 <property name="text">
2044 <string>Left</string>
2045 </property>
2046 </widget>
2047 </item>
2048 </layout>
2049 </widget>
2050 </item>
2051 <item>
2052 <widget class="QGroupBox" name="buttonMotionRightGroup">
2053 <property name="title">
2054 <string>Motion 2</string>
2055 </property>
2056 <property name="alignment">
2057 <set>Qt::AlignCenter</set>
2058 </property>
2059 <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
2060 <property name="spacing">
2061 <number>3</number>
2062 </property>
2063 <property name="leftMargin">
2064 <number>3</number>
2065 </property>
2066 <property name="topMargin">
2067 <number>3</number>
2068 </property>
2069 <property name="rightMargin">
2070 <number>3</number>
2071 </property>
2072 <property name="bottomMargin">
2073 <number>3</number>
2074 </property>
2075 <item>
2076 <widget class="QPushButton" name="buttonMotionRight">
2077 <property name="minimumSize">
2078 <size>
2079 <width>57</width>
2080 <height>0</height>
2081 </size>
2082 </property>
2083 <property name="maximumSize">
2084 <size>
2085 <width>55</width>
2086 <height>16777215</height>
2087 </size>
2088 </property>
2089 <property name="styleSheet">
2090 <string notr="true">min-width: 55px;</string>
2091 </property>
2092 <property name="text">
2093 <string>Right</string>
2094 </property>
2095 </widget>
2096 </item>
2097 </layout>
2098 </widget>
2099 </item>
2100 <item>
2000 <spacer name="horizontalSpacerMiscButtons4"> 2101 <spacer name="horizontalSpacerMiscButtons4">
2001 <property name="orientation"> 2102 <property name="orientation">
2002 <enum>Qt::Horizontal</enum> 2103 <enum>Qt::Horizontal</enum>
2003 </property> 2104 </property>
2004 <property name="sizeHint" stdset="0"> 2105 <property name="sizeHint" stdset="0">
2005 <size> 2106 <size>
2006 <width>40</width> 2107 <width>20</width>
2007 <height>0</height> 2108 <height>20</height>
2008 </size> 2109 </size>
2009 </property> 2110 </property>
2010 </spacer> 2111 </spacer>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 68ad43a80..bb3a08ac7 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2592,8 +2592,10 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2592 2592
2593 const auto function = [this, &keys, &pdm] { 2593 const auto function = [this, &keys, &pdm] {
2594 keys.PopulateFromPartitionData(pdm); 2594 keys.PopulateFromPartitionData(pdm);
2595 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs); 2595
2596 keys.DeriveETicket(pdm); 2596 auto& system = Core::System::GetInstance();
2597 system.GetFileSystemController().CreateFactories(*vfs);
2598 keys.DeriveETicket(pdm, system.GetContentProvider());
2597 }; 2599 };
2598 2600
2599 QString errors; 2601 QString errors;
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index e9f1c6500..23448e747 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -290,6 +290,8 @@ void Config::ReadValues() {
290 290
291 Settings::values.vibration_enabled = 291 Settings::values.vibration_enabled =
292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true); 292 sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true);
293 Settings::values.motion_enabled =
294 sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true);
293 Settings::values.touchscreen.enabled = 295 Settings::values.touchscreen.enabled =
294 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); 296 sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
295 Settings::values.touchscreen.device = 297 Settings::values.touchscreen.device =
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index aaf59129a..bc273fb51 100644
--- a/src/yuzu_tester/config.cpp
+++ b/src/yuzu_tester/config.cpp
@@ -76,6 +76,7 @@ void Config::ReadValues() {
76 } 76 }
77 77
78 Settings::values.vibration_enabled = true; 78 Settings::values.vibration_enabled = true;
79 Settings::values.motion_enabled = true;
79 Settings::values.touchscreen.enabled = ""; 80 Settings::values.touchscreen.enabled = "";
80 Settings::values.touchscreen.device = ""; 81 Settings::values.touchscreen.device = "";
81 Settings::values.touchscreen.finger = 0; 82 Settings::values.touchscreen.finger = 0;