diff options
97 files changed, 1532 insertions, 894 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 | ||
| 215 | void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 215 | void 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 | ||
| 407 | void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) { | 407 | void 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 | ||
| 675 | s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 675 | s32 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 | ||
| 715 | s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 715 | s32 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 | ||
| 632 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 632 | void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { |
| 633 | impl->virtual_filesystem = std::move(vfs); | 633 | impl->virtual_filesystem = std::move(vfs); |
| 634 | } | 634 | } |
| 635 | 635 | ||
| 636 | std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | 636 | FileSys::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 | ||
| 1025 | void KeyManager::DeriveETicket(PartitionDataManager& data) { | 1024 | void 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 { | |||
| 20 | class IOFile; | 20 | class IOFile; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | namespace FileSys { | ||
| 24 | class ContentProvider; | ||
| 25 | } | ||
| 26 | |||
| 23 | namespace Loader { | 27 | namespace Loader { |
| 24 | enum class ResultStatus : u16; | 28 | enum 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 | ||
| 12 | namespace FileSys { | 12 | namespace FileSys { |
| 13 | 13 | ||
| @@ -81,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | |||
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | 84 | VirtualFile 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 | ||
| 14 | namespace FileSys { | 14 | namespace FileSys { |
| 15 | namespace { | ||
| 16 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 17 | std::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 | ||
| 16 | BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, | 59 | BKTR::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 | ||
| 113 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 114 | std::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 | |||
| 153 | RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { | 156 | RelocationEntry 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 | ||
| 119 | private: | 119 | private: |
| 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 | ||
| 29 | namespace FileSys { | 29 | namespace FileSys { |
| 30 | namespace { | ||
| 30 | 31 | ||
| 31 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 32 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; |
| 32 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 33 | constexpr 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 | ||
| 39 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 40 | enum class TitleVersionFormat : u8 { |
| 41 | ThreeElements, ///< vX.Y.Z | ||
| 42 | FourElements, ///< vX.Y.Z.W | ||
| 43 | }; | ||
| 44 | |||
| 45 | std::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. | ||
| 52 | VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { | 62 | VirtualDir 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 | ||
| 78 | std::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 | |||
| 101 | void 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 | |||
| 110 | bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 111 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 112 | } | ||
| 113 | } // Anonymous namespace | ||
| 114 | |||
| 68 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 115 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} |
| 69 | 116 | ||
| 70 | PatchManager::~PatchManager() = default; | 117 | PatchManager::~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 | ||
| 248 | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | 295 | bool 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 | ||
| 268 | namespace { | ||
| 269 | std::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 | |||
| 296 | std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( | 315 | std::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 | ||
| 438 | static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { | 455 | PatchManager::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 | |||
| 445 | static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 446 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 447 | } | ||
| 448 | |||
| 449 | std::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 | ||
| 565 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 571 | PatchManager::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 | ||
| 575 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { | 582 | PatchManager::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 { | |||
| 22 | class NCA; | 22 | class NCA; |
| 23 | class NACP; | 23 | class NACP; |
| 24 | 24 | ||
| 25 | enum class TitleVersionFormat : u8 { | ||
| 26 | ThreeElements, ///< vX.Y.Z | ||
| 27 | FourElements, ///< vX.Y.Z.W | ||
| 28 | }; | ||
| 29 | |||
| 30 | std::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. | ||
| 35 | VirtualDir 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. |
| 38 | class PatchManager { | 26 | class PatchManager { |
| 39 | public: | 27 | public: |
| 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 | ||
| 86 | private: | 78 | private: |
| 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 | ||
| 20 | namespace FileSys { | 19 | namespace FileSys { |
| 21 | 20 | ||
| 22 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | 21 | RomFSFactory::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 | ||
| 47 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, | 48 | ResultVal<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 | ||
| 65 | std::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 { | |||
| 13 | class AppLoader; | 13 | class AppLoader; |
| 14 | } // namespace Loader | 14 | } // namespace Loader |
| 15 | 15 | ||
| 16 | namespace Service::FileSystem { | ||
| 17 | class FileSystemController; | ||
| 18 | } | ||
| 19 | |||
| 16 | namespace FileSys { | 20 | namespace FileSys { |
| 17 | 21 | ||
| 22 | class ContentProvider; | ||
| 23 | class NCA; | ||
| 24 | |||
| 18 | enum class ContentRecordType : u8; | 25 | enum class ContentRecordType : u8; |
| 19 | 26 | ||
| 20 | enum class StorageId : u8 { | 27 | enum 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 |
| 30 | class RomFSFactory { | 37 | class RomFSFactory { |
| 31 | public: | 38 | public: |
| 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 | ||
| 39 | private: | 48 | private: |
| 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>; | |||
| 119 | using AnalogDevice = InputDevice<std::tuple<float, float>>; | 119 | using 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 | */ | ||
| 145 | using 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 | */ |
| 137 | using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; | 151 | using 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 | ||
| 50 | ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, | 50 | ResultCode 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 { | |||
| 16 | class Memory; | 16 | class Memory; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core::Timing { | ||
| 20 | class CoreTiming; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace Kernel { | 23 | namespace Kernel { |
| 20 | 24 | ||
| 21 | class KernelCore; | 25 | class 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 | ||
| 187 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 186 | ResultCode 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 | ||
| 20 | namespace Core::Timing { | 20 | namespace Core::Timing { |
| 21 | class CoreTiming; | ||
| 21 | struct EventType; | 22 | struct EventType; |
| 22 | } | 23 | } // namespace Core::Timing |
| 23 | 24 | ||
| 24 | namespace Kernel { | 25 | namespace 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 | ||
| 1557 | void 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 | |||
| 1557 | void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { | 1565 | void 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 7818c098f..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; |
| @@ -495,6 +593,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { | |||
| 495 | return hold_type; | 593 | return hold_type; |
| 496 | } | 594 | } |
| 497 | 595 | ||
| 596 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | ||
| 597 | handheld_activation_mode = activation_mode; | ||
| 598 | } | ||
| 599 | |||
| 600 | Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { | ||
| 601 | return handheld_activation_mode; | ||
| 602 | } | ||
| 603 | |||
| 498 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { | 604 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { |
| 499 | const std::size_t npad_index = NPadIdToIndex(npad_id); | 605 | const std::size_t npad_index = NPadIdToIndex(npad_id); |
| 500 | ASSERT(npad_index < shared_memory_entries.size()); | 606 | ASSERT(npad_index < shared_memory_entries.size()); |
| @@ -582,6 +688,14 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo | |||
| 582 | return gyroscope_zero_drift_mode; | 688 | return gyroscope_zero_drift_mode; |
| 583 | } | 689 | } |
| 584 | 690 | ||
| 691 | bool Controller_NPad::IsSixAxisSensorAtRest() const { | ||
| 692 | return sixaxis_at_rest; | ||
| 693 | } | ||
| 694 | |||
| 695 | void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { | ||
| 696 | sixaxis_sensors_enabled = six_axis_status; | ||
| 697 | } | ||
| 698 | |||
| 585 | void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { | 699 | void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { |
| 586 | const auto npad_index_1 = NPadIdToIndex(npad_id_1); | 700 | const auto npad_index_1 = NPadIdToIndex(npad_id_1); |
| 587 | 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 e9788da8d..654d97c3f 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -74,6 +74,12 @@ public: | |||
| 74 | Single = 1, | 74 | Single = 1, |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | enum class NpadHandheldActivationMode : u64 { | ||
| 78 | Dual = 0, | ||
| 79 | Single = 1, | ||
| 80 | None = 2, | ||
| 81 | }; | ||
| 82 | |||
| 77 | enum class NPadControllerType { | 83 | enum class NPadControllerType { |
| 78 | None, | 84 | None, |
| 79 | ProController, | 85 | ProController, |
| @@ -110,6 +116,9 @@ public: | |||
| 110 | void SetHoldType(NpadHoldType joy_hold_type); | 116 | void SetHoldType(NpadHoldType joy_hold_type); |
| 111 | NpadHoldType GetHoldType() const; | 117 | NpadHoldType GetHoldType() const; |
| 112 | 118 | ||
| 119 | void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); | ||
| 120 | NpadHandheldActivationMode GetNpadHandheldActivationMode() const; | ||
| 121 | |||
| 113 | void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); | 122 | void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); |
| 114 | 123 | ||
| 115 | void VibrateController(const std::vector<u32>& controller_ids, | 124 | void VibrateController(const std::vector<u32>& controller_ids, |
| @@ -130,6 +139,8 @@ public: | |||
| 130 | 139 | ||
| 131 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); | 140 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); |
| 132 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; | 141 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; |
| 142 | bool IsSixAxisSensorAtRest() const; | ||
| 143 | void SetSixAxisEnabled(bool six_axis_status); | ||
| 133 | LedPattern GetLedPattern(u32 npad_id); | 144 | LedPattern GetLedPattern(u32 npad_id); |
| 134 | void SetVibrationEnabled(bool can_vibrate); | 145 | void SetVibrationEnabled(bool can_vibrate); |
| 135 | bool IsVibrationEnabled() const; | 146 | bool IsVibrationEnabled() const; |
| @@ -252,6 +263,24 @@ private: | |||
| 252 | }; | 263 | }; |
| 253 | static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); | 264 | static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); |
| 254 | 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 | |||
| 255 | enum class ColorReadError : u32_le { | 284 | enum class ColorReadError : u32_le { |
| 256 | ReadOk = 0, | 285 | ReadOk = 0, |
| 257 | ColorDoesntExist = 1, | 286 | ColorDoesntExist = 1, |
| @@ -281,6 +310,13 @@ private: | |||
| 281 | }; | 310 | }; |
| 282 | }; | 311 | }; |
| 283 | 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 | |||
| 284 | struct NPadEntry { | 320 | struct NPadEntry { |
| 285 | NPadType joy_styles; | 321 | NPadType joy_styles; |
| 286 | NPadAssignments pad_assignment; | 322 | NPadAssignments pad_assignment; |
| @@ -300,9 +336,12 @@ private: | |||
| 300 | NPadGeneric pokeball_states; | 336 | NPadGeneric pokeball_states; |
| 301 | 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 |
| 302 | // relying on this for the time being | 338 | // relying on this for the time being |
| 303 | INSERT_PADDING_BYTES( | 339 | SixAxisGeneric sixaxis_full; |
| 304 | 0x708 * | 340 | SixAxisGeneric sixaxis_handheld; |
| 305 | 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; | ||
| 306 | NPadDevice device_type; | 345 | NPadDevice device_type; |
| 307 | NPadProperties properties; | 346 | NPadProperties properties; |
| 308 | INSERT_PADDING_WORDS(1); | 347 | INSERT_PADDING_WORDS(1); |
| @@ -325,22 +364,29 @@ private: | |||
| 325 | 364 | ||
| 326 | NPadType style{}; | 365 | NPadType style{}; |
| 327 | std::array<NPadEntry, 10> shared_memory_entries{}; | 366 | std::array<NPadEntry, 10> shared_memory_entries{}; |
| 328 | std::array< | 367 | using ButtonArray = std::array< |
| 329 | 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>, |
| 330 | 10> | 369 | 10>; |
| 331 | buttons; | 370 | using StickArray = std::array< |
| 332 | std::array< | ||
| 333 | 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>, |
| 334 | 10> | 372 | 10>; |
| 335 | 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; | ||
| 336 | std::vector<u32> supported_npad_id_types{}; | 379 | std::vector<u32> supported_npad_id_types{}; |
| 337 | NpadHoldType hold_type{NpadHoldType::Vertical}; | 380 | NpadHoldType hold_type{NpadHoldType::Vertical}; |
| 381 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; | ||
| 338 | // Each controller should have their own styleset changed event | 382 | // Each controller should have their own styleset changed event |
| 339 | std::array<Kernel::EventPair, 10> styleset_changed_events; | 383 | std::array<Kernel::EventPair, 10> styleset_changed_events; |
| 340 | Vibration last_processed_vibration{}; | 384 | Vibration last_processed_vibration{}; |
| 341 | std::array<ControllerHolder, 10> connected_controllers{}; | 385 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 342 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | 386 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; |
| 343 | bool can_controllers_vibrate{true}; | 387 | bool can_controllers_vibrate{true}; |
| 388 | bool sixaxis_sensors_enabled{true}; | ||
| 389 | bool sixaxis_at_rest{true}; | ||
| 344 | std::array<ControllerPad, 10> npad_pad_states{}; | 390 | std::array<ControllerPad, 10> npad_pad_states{}; |
| 345 | bool is_in_lr_assignment_mode{false}; | 391 | bool is_in_lr_assignment_mode{false}; |
| 346 | 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 bd3c2f26b..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 |
| 43 | constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz) | 43 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) |
| 44 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 44 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 45 | 45 | ||
| 46 | IAppletResource::IAppletResource(Core::System& system) | 46 | IAppletResource::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 | ||
| 332 | void 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 | |||
| 344 | void 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 | |||
| 332 | void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { | 357 | void 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 | ||
| 496 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { | 521 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { |
| @@ -714,8 +739,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { | |||
| 714 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 739 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 715 | const auto mode{rp.Pop<u64>()}; | 740 | const auto mode{rp.Pop<u64>()}; |
| 716 | 741 | ||
| 717 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", | 742 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, |
| 718 | applet_resource_user_id, mode); | 743 | mode); |
| 744 | |||
| 745 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 746 | .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); | ||
| 719 | 747 | ||
| 720 | IPC::ResponseBuilder rb{ctx, 2}; | 748 | IPC::ResponseBuilder rb{ctx, 2}; |
| 721 | rb.Push(RESULT_SUCCESS); | 749 | rb.Push(RESULT_SUCCESS); |
| @@ -725,11 +753,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { | |||
| 725 | IPC::RequestParser rp{ctx}; | 753 | IPC::RequestParser rp{ctx}; |
| 726 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 754 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 727 | 755 | ||
| 728 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | 756 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 729 | applet_resource_user_id); | ||
| 730 | 757 | ||
| 731 | IPC::ResponseBuilder rb{ctx, 2}; | 758 | IPC::ResponseBuilder rb{ctx, 4}; |
| 732 | rb.Push(RESULT_SUCCESS); | 759 | rb.Push(RESULT_SUCCESS); |
| 760 | rb.Push<u64>( | ||
| 761 | static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 762 | .GetNpadHandheldActivationMode())); | ||
| 733 | } | 763 | } |
| 734 | 764 | ||
| 735 | void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { | 765 | void Hid::SwapNpadAssignment(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 | ||
| 73 | namespace Service { | 73 | namespace 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 | |||
| 94 | ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, | 75 | ServiceFrameworkBase::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 | ||
| 108 | void ServiceFrameworkBase::InstallAsNamedPort() { | 89 | void 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 | ||
| 119 | std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | 99 | std::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 |
| 198 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | 174 | void 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); | |||
| 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); | 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); |
| 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); | 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); |
| 21 | 21 | ||
| 22 | ServiceManager::ServiceManager() = default; | 22 | ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} |
| 23 | ServiceManager::~ServiceManager() = default; | 23 | ServiceManager::~ServiceManager() = default; |
| 24 | 24 | ||
| 25 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | 25 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { |
| @@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | |||
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | static ResultCode ValidateServiceName(const std::string& name) { | 29 | static 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 | ||
| 51 | ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( | 51 | ResultVal<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 { | |||
| 48 | public: | 48 | public: |
| 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 | ||
| 91 | AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( | 91 | AppLoader_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 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace Loader { | 16 | namespace 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 | ||
| 386 | AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { | 386 | AppLoader_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 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Loader { | 15 | namespace 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 | ||
| 46 | AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { | 46 | AppLoader::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 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace FileSys { | 13 | namespace FileSys { |
| 10 | class KIP; | 14 | class 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 | ||
| 31 | private: | 35 | private: |
| 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 | ||
| 18 | namespace Core { | ||
| 19 | class System; | ||
| 20 | } | ||
| 21 | |||
| 18 | namespace FileSys { | 22 | namespace FileSys { |
| 19 | class NACP; | 23 | class 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 | ||
| 44 | AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { | 44 | AppLoader_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 | ||
| 11 | namespace FileSys { | 11 | namespace Core { |
| 12 | class System; | ||
| 13 | } | ||
| 12 | 14 | ||
| 15 | namespace FileSys { | ||
| 13 | class NAX; | 16 | class NAX; |
| 14 | |||
| 15 | } // namespace FileSys | 17 | } // namespace FileSys |
| 16 | 18 | ||
| 17 | namespace Loader { | 19 | namespace 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 | ||
| 34 | AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { | 34 | AppLoader_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 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace FileSys { | 15 | namespace FileSys { |
| 12 | class NCA; | 16 | class 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 | ||
| 211 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | 211 | AppLoader_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 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace FileSys { | 17 | namespace FileSys { |
| 14 | class NACP; | 18 | class 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 | ||
| 74 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | 74 | std::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 | ||
| 169 | AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | 168 | AppLoader_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 | ||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Kernel { | 19 | namespace Kernel { |
| 16 | class Process; | 20 | class 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 | ||
| 74 | AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | 74 | AppLoader_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 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace FileSys { | 16 | namespace FileSys { |
| 13 | class NACP; | 17 | class NACP; |
| 14 | class NSP; | 18 | class 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 | ||
| 52 | AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { | 52 | AppLoader_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 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace FileSys { | 16 | namespace FileSys { |
| 13 | class NACP; | 17 | class NACP; |
| 14 | class XCI; | 18 | class 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 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | namespace { | |
| 23 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; | 23 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | std::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 | |||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 40 | StandardVmCallbacks::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 | ||
| 83 | TextCheatParser::~TextCheatParser() = default; | 97 | TextCheatParser::~TextCheatParser() = default; |
| 84 | 98 | ||
| 85 | namespace { | 99 | std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const { |
| 86 | template <char match> | ||
| 87 | std::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 | |||
| 101 | std::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 { | |||
| 47 | public: | 47 | public: |
| 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 { | |||
| 56 | public: | 55 | public: |
| 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 | ||
| 122 | InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {} | 146 | InputSubsystem::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 | ||
| 202 | UDPMotionFactory* InputSubsystem::GetUDPMotions() { | ||
| 203 | return impl->udpmotion.get(); | ||
| 204 | } | ||
| 205 | |||
| 206 | const UDPMotionFactory* InputSubsystem::GetUDPMotions() const { | ||
| 207 | return impl->udpmotion.get(); | ||
| 208 | } | ||
| 209 | |||
| 210 | UDPTouchFactory* InputSubsystem::GetUDPTouch() { | ||
| 211 | return impl->udptouch.get(); | ||
| 212 | } | ||
| 213 | |||
| 214 | const UDPTouchFactory* InputSubsystem::GetUDPTouch() const { | ||
| 215 | return impl->udptouch.get(); | ||
| 216 | } | ||
| 217 | |||
| 178 | void InputSubsystem::ReloadInputDevices() { | 218 | void 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 { | |||
| 21 | enum Values : int; | 21 | enum Values : int; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | namespace Settings::NativeMotion { | ||
| 25 | enum Values : int; | ||
| 26 | } | ||
| 27 | |||
| 24 | namespace InputCommon { | 28 | namespace InputCommon { |
| 25 | namespace Polling { | 29 | namespace Polling { |
| 26 | 30 | ||
| 27 | enum class DeviceType { Button, AnalogPreferred }; | 31 | enum 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 | ||
| 51 | class GCAnalogFactory; | 55 | class GCAnalogFactory; |
| 52 | class GCButtonFactory; | 56 | class GCButtonFactory; |
| 57 | class UDPMotionFactory; | ||
| 58 | class UDPTouchFactory; | ||
| 53 | class Keyboard; | 59 | class Keyboard; |
| 54 | class MotionEmu; | 60 | class MotionEmu; |
| 55 | 61 | ||
| @@ -59,6 +65,7 @@ class MotionEmu; | |||
| 59 | */ | 65 | */ |
| 60 | using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; | 66 | using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>; |
| 61 | using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; | 67 | using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>; |
| 68 | using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>; | ||
| 62 | 69 | ||
| 63 | class InputSubsystem { | 70 | class InputSubsystem { |
| 64 | public: | 71 | public: |
| @@ -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 | ||
| 17 | namespace NativeMotion { | ||
| 18 | const std::array<const char*, NumMotions> mapping = {{ | ||
| 19 | "motionleft", | ||
| 20 | "motionright", | ||
| 21 | }}; | ||
| 22 | } | ||
| 23 | |||
| 17 | namespace NativeAnalog { | 24 | namespace NativeAnalog { |
| 18 | const std::array<const char*, NumAnalogs> mapping = {{ | 25 | const 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; | |||
| 66 | extern const std::array<const char*, NumAnalogs> mapping; | 66 | extern const std::array<const char*, NumAnalogs> mapping; |
| 67 | } // namespace NativeAnalog | 67 | } // namespace NativeAnalog |
| 68 | 68 | ||
| 69 | namespace NativeMotion { | ||
| 70 | enum Values : int { | ||
| 71 | MOTIONLEFT, | ||
| 72 | MOTIONRIGHT, | ||
| 73 | |||
| 74 | NumMotions, | ||
| 75 | }; | ||
| 76 | |||
| 77 | constexpr int MOTION_HID_BEGIN = MOTIONLEFT; | ||
| 78 | constexpr int MOTION_HID_END = NumMotions; | ||
| 79 | constexpr int NUM_MOTION_HID = NumMotions; | ||
| 80 | |||
| 81 | extern const std::array<const char*, NumMotions> mapping; | ||
| 82 | } // namespace NativeMotion | ||
| 83 | |||
| 69 | namespace NativeMouseButton { | 84 | namespace NativeMouseButton { |
| 70 | enum Values { | 85 | enum Values { |
| 71 | Left, | 86 | Left, |
| @@ -292,6 +307,7 @@ constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; | |||
| 292 | 307 | ||
| 293 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; | 308 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; |
| 294 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; | 309 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; |
| 310 | using MotionRaw = std::array<std::string, NativeMotion::NumMotions>; | ||
| 295 | using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; | 311 | using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; |
| 296 | using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; | 312 | using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; |
| 297 | using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; | 313 | using 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 | ||
| 134 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, | 133 | Client::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 | ||
| 140 | Client::~Client() { | 146 | Client::~Client() { |
| 141 | socket->Stop(); | 147 | Reset(); |
| 142 | thread.join(); | 148 | } |
| 149 | |||
| 150 | std::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 | ||
| 166 | bool 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 | |||
| 175 | void 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 | } | ||
| 145 | void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | 180 | void 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 | ||
| 151 | void Client::OnVersion(Response::Version data) { | 188 | void Client::OnVersion(Response::Version data) { |
| @@ -157,23 +194,39 @@ void Client::OnPortInfo(Response::PortInfo data) { | |||
| 157 | } | 194 | } |
| 158 | 195 | ||
| 159 | void Client::OnPadData(Response::PadData data) { | 196 | void 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 | ||
| 201 | void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | 258 | void 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 | |||
| 268 | void 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 | |||
| 275 | void 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 | |||
| 296 | void Client::BeginConfiguration() { | ||
| 297 | for (auto& pq : pad_queue) { | ||
| 298 | pq.Clear(); | ||
| 299 | } | ||
| 300 | configuring = true; | ||
| 301 | } | ||
| 302 | |||
| 303 | void Client::EndConfiguration() { | ||
| 304 | for (auto& pq : pad_queue) { | ||
| 305 | pq.Clear(); | ||
| 306 | } | ||
| 307 | configuring = false; | ||
| 308 | } | ||
| 309 | |||
| 310 | DeviceStatus& Client::GetPadState(std::size_t pad) { | ||
| 311 | return clients[pad].status; | ||
| 312 | } | ||
| 313 | |||
| 314 | const DeviceStatus& Client::GetPadState(std::size_t pad) const { | ||
| 315 | return clients[pad].status; | ||
| 316 | } | ||
| 317 | |||
| 318 | std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() { | ||
| 319 | return pad_queue; | ||
| 320 | } | ||
| 321 | |||
| 322 | const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const { | ||
| 323 | return pad_queue; | ||
| 208 | } | 324 | } |
| 209 | 325 | ||
| 210 | void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, | 326 | void 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 | ||
| 18 | namespace InputCommon::CemuhookUDP { | 22 | namespace InputCommon::CemuhookUDP { |
| 19 | 23 | ||
| @@ -28,9 +32,30 @@ struct PortInfo; | |||
| 28 | struct Version; | 32 | struct Version; |
| 29 | } // namespace Response | 33 | } // namespace Response |
| 30 | 34 | ||
| 35 | enum class PadMotion { | ||
| 36 | GyroX, | ||
| 37 | GyroY, | ||
| 38 | GyroZ, | ||
| 39 | AccX, | ||
| 40 | AccY, | ||
| 41 | AccZ, | ||
| 42 | Undefined, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum class PadTouch { | ||
| 46 | Click, | ||
| 47 | Undefined, | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct UDPPadStatus { | ||
| 51 | PadTouch touch{PadTouch::Undefined}; | ||
| 52 | PadMotion motion{PadMotion::Undefined}; | ||
| 53 | f32 motion_value{0.0f}; | ||
| 54 | }; | ||
| 55 | |||
| 31 | struct DeviceStatus { | 56 | struct 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 | ||
| 46 | class Client { | 71 | class Client { |
| 47 | public: | 72 | public: |
| 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 | |||
| 54 | private: | 96 | private: |
| 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 | ||
| 15 | namespace InputCommon::CemuhookUDP { | 14 | namespace InputCommon { |
| 16 | 15 | ||
| 17 | class UDPTouchDevice final : public Input::TouchDevice { | 16 | class UDPMotion final : public Input::MotionDevice { |
| 18 | public: | 17 | public: |
| 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 | ||
| 25 | private: | 25 | private: |
| 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 | ||
| 29 | class UDPMotionDevice final : public Input::MotionDevice { | 33 | /// A motion device factory that creates motion devices from JC Adapter |
| 30 | public: | 34 | UDPMotionFactory::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 | */ | ||
| 42 | std::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 | ||
| 37 | private: | 50 | void UDPMotionFactory::BeginConfiguration() { |
| 38 | std::shared_ptr<DeviceStatus> status; | 51 | polling = true; |
| 39 | }; | 52 | client->BeginConfiguration(); |
| 53 | } | ||
| 40 | 54 | ||
| 41 | class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | 55 | void UDPMotionFactory::EndConfiguration() { |
| 42 | public: | 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 | { | 60 | Common::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 | ||
| 58 | private: | 80 | class UDPTouch final : public Input::TouchDevice { |
| 59 | std::shared_ptr<DeviceStatus> status; | ||
| 60 | }; | ||
| 61 | |||
| 62 | class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||
| 63 | public: | 81 | public: |
| 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 | ||
| 70 | private: | 89 | private: |
| 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 | ||
| 74 | State::State() { | 97 | /// A motion device factory that creates motion devices from JC Adapter |
| 75 | auto status = std::make_shared<DeviceStatus>(); | 98 | UDPTouchFactory::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); | 106 | std::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 | ||
| 87 | State::~State() { | 114 | void UDPTouchFactory::BeginConfiguration() { |
| 88 | Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); | 115 | polling = true; |
| 89 | Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); | 116 | client->BeginConfiguration(); |
| 90 | } | 117 | } |
| 91 | 118 | ||
| 92 | std::vector<Common::ParamPackage> State::GetInputDevices() const { | 119 | void UDPTouchFactory::EndConfiguration() { |
| 93 | // TODO support binding udp devices | 120 | polling = false; |
| 94 | return {}; | 121 | client->EndConfiguration(); |
| 95 | } | 122 | } |
| 96 | 123 | ||
| 97 | void State::ReloadUDPClient() { | 124 | Common::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 | ||
| 102 | std::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 | ||
| 11 | namespace InputCommon::CemuhookUDP { | 11 | namespace InputCommon { |
| 12 | 12 | ||
| 13 | class Client; | 13 | /// A motion device factory that creates motion devices from udp clients |
| 14 | class UDPMotionFactory; | 14 | class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { |
| 15 | class UDPTouchFactory; | ||
| 16 | |||
| 17 | class State { | ||
| 18 | public: | 15 | public: |
| 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 | ||
| 24 | private: | 30 | private: |
| 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 | ||
| 30 | std::unique_ptr<State> Init(); | 35 | /// A touch device factory that creates touch devices from udp clients |
| 36 | class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||
| 37 | public: | ||
| 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 | |||
| 52 | private: | ||
| 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() | |||
| 269 | if (MSVC) | 269 | if (MSVC) |
| 270 | target_compile_options(video_core PRIVATE /we4267) | 270 | target_compile_options(video_core PRIVATE /we4267) |
| 271 | else() | 271 | else() |
| 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) |
| 273 | endif() | 273 | endif() |
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_base.h b/src/video_core/renderer_base.h index 649074acd..5c650808b 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h | |||
| @@ -46,11 +46,6 @@ public: | |||
| 46 | /// Finalize rendering the guest frame and draw into the presentation texture | 46 | /// Finalize rendering the guest frame and draw into the presentation texture |
| 47 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | 47 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; |
| 48 | 48 | ||
| 49 | /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer | ||
| 50 | /// specific implementation) | ||
| 51 | /// Returns true if a frame was drawn | ||
| 52 | virtual bool TryPresent(int timeout_ms) = 0; | ||
| 53 | |||
| 54 | // Getter/setter functions: | 49 | // Getter/setter functions: |
| 55 | // ------------------------ | 50 | // ------------------------ |
| 56 | 51 | ||
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_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index a4c5b8f74..2ccca1993 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -32,20 +32,6 @@ namespace OpenGL { | |||
| 32 | 32 | ||
| 33 | namespace { | 33 | namespace { |
| 34 | 34 | ||
| 35 | constexpr std::size_t SWAP_CHAIN_SIZE = 3; | ||
| 36 | |||
| 37 | struct Frame { | ||
| 38 | u32 width{}; /// Width of the frame (to detect resize) | ||
| 39 | u32 height{}; /// Height of the frame | ||
| 40 | bool color_reloaded{}; /// Texture attachment was recreated (ie: resized) | ||
| 41 | OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO | ||
| 42 | OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread | ||
| 43 | OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread | ||
| 44 | GLsync render_fence{}; /// Fence created on the render thread | ||
| 45 | GLsync present_fence{}; /// Fence created on the presentation thread | ||
| 46 | bool is_srgb{}; /// Framebuffer is sRGB or RGB | ||
| 47 | }; | ||
| 48 | |||
| 49 | constexpr GLint PositionLocation = 0; | 35 | constexpr GLint PositionLocation = 0; |
| 50 | constexpr GLint TexCoordLocation = 1; | 36 | constexpr GLint TexCoordLocation = 1; |
| 51 | constexpr GLint ModelViewMatrixLocation = 0; | 37 | constexpr GLint ModelViewMatrixLocation = 0; |
| @@ -58,24 +44,6 @@ struct ScreenRectVertex { | |||
| 58 | std::array<GLfloat, 2> tex_coord; | 44 | std::array<GLfloat, 2> tex_coord; |
| 59 | }; | 45 | }; |
| 60 | 46 | ||
| 61 | /// Returns true if any debug tool is attached | ||
| 62 | bool HasDebugTool() { | ||
| 63 | const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); | ||
| 64 | if (nsight) { | ||
| 65 | return true; | ||
| 66 | } | ||
| 67 | |||
| 68 | GLint num_extensions; | ||
| 69 | glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); | ||
| 70 | for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) { | ||
| 71 | const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index)); | ||
| 72 | if (!std::strcmp(name, "GL_EXT_debug_tool")) { | ||
| 73 | return true; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | return false; | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | 47 | /** |
| 80 | * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left | 48 | * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left |
| 81 | * corner and (width, height) on the lower-bottom. | 49 | * corner and (width, height) on the lower-bottom. |
| @@ -159,135 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit | |||
| 159 | 127 | ||
| 160 | } // Anonymous namespace | 128 | } // Anonymous namespace |
| 161 | 129 | ||
| 162 | /** | ||
| 163 | * For smooth Vsync rendering, we want to always present the latest frame that the core generates, | ||
| 164 | * but also make sure that rendering happens at the pace that the frontend dictates. This is a | ||
| 165 | * helper class that the renderer uses to sync frames between the render thread and the presentation | ||
| 166 | * thread | ||
| 167 | */ | ||
| 168 | class FrameMailbox { | ||
| 169 | public: | ||
| 170 | std::mutex swap_chain_lock; | ||
| 171 | std::condition_variable present_cv; | ||
| 172 | std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{}; | ||
| 173 | std::queue<Frame*> free_queue; | ||
| 174 | std::deque<Frame*> present_queue; | ||
| 175 | Frame* previous_frame{}; | ||
| 176 | |||
| 177 | FrameMailbox() { | ||
| 178 | for (auto& frame : swap_chain) { | ||
| 179 | free_queue.push(&frame); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | ~FrameMailbox() { | ||
| 184 | // lock the mutex and clear out the present and free_queues and notify any people who are | ||
| 185 | // blocked to prevent deadlock on shutdown | ||
| 186 | std::scoped_lock lock{swap_chain_lock}; | ||
| 187 | std::queue<Frame*>().swap(free_queue); | ||
| 188 | present_queue.clear(); | ||
| 189 | present_cv.notify_all(); | ||
| 190 | } | ||
| 191 | |||
| 192 | void ReloadPresentFrame(Frame* frame, u32 height, u32 width) { | ||
| 193 | frame->present.Release(); | ||
| 194 | frame->present.Create(); | ||
| 195 | GLint previous_draw_fbo{}; | ||
| 196 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo); | ||
| 197 | glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle); | ||
| 198 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 199 | frame->color.handle); | ||
| 200 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||
| 201 | LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!"); | ||
| 202 | } | ||
| 203 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo); | ||
| 204 | frame->color_reloaded = false; | ||
| 205 | } | ||
| 206 | |||
| 207 | void ReloadRenderFrame(Frame* frame, u32 width, u32 height) { | ||
| 208 | // Recreate the color texture attachment | ||
| 209 | frame->color.Release(); | ||
| 210 | frame->color.Create(); | ||
| 211 | const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8; | ||
| 212 | glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height); | ||
| 213 | |||
| 214 | // Recreate the FBO for the render target | ||
| 215 | frame->render.Release(); | ||
| 216 | frame->render.Create(); | ||
| 217 | glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle); | ||
| 218 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, | ||
| 219 | frame->color.handle); | ||
| 220 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | ||
| 221 | LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!"); | ||
| 222 | } | ||
| 223 | |||
| 224 | frame->width = width; | ||
| 225 | frame->height = height; | ||
| 226 | frame->color_reloaded = true; | ||
| 227 | } | ||
| 228 | |||
| 229 | Frame* GetRenderFrame() { | ||
| 230 | std::unique_lock lock{swap_chain_lock}; | ||
| 231 | |||
| 232 | // If theres no free frames, we will reuse the oldest render frame | ||
| 233 | if (free_queue.empty()) { | ||
| 234 | auto frame = present_queue.back(); | ||
| 235 | present_queue.pop_back(); | ||
| 236 | return frame; | ||
| 237 | } | ||
| 238 | |||
| 239 | Frame* frame = free_queue.front(); | ||
| 240 | free_queue.pop(); | ||
| 241 | return frame; | ||
| 242 | } | ||
| 243 | |||
| 244 | void ReleaseRenderFrame(Frame* frame) { | ||
| 245 | std::unique_lock lock{swap_chain_lock}; | ||
| 246 | present_queue.push_front(frame); | ||
| 247 | present_cv.notify_one(); | ||
| 248 | } | ||
| 249 | |||
| 250 | Frame* TryGetPresentFrame(int timeout_ms) { | ||
| 251 | std::unique_lock lock{swap_chain_lock}; | ||
| 252 | // wait for new entries in the present_queue | ||
| 253 | present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), | ||
| 254 | [&] { return !present_queue.empty(); }); | ||
| 255 | if (present_queue.empty()) { | ||
| 256 | // timed out waiting for a frame to draw so return the previous frame | ||
| 257 | return previous_frame; | ||
| 258 | } | ||
| 259 | |||
| 260 | // free the previous frame and add it back to the free queue | ||
| 261 | if (previous_frame) { | ||
| 262 | free_queue.push(previous_frame); | ||
| 263 | } | ||
| 264 | |||
| 265 | // the newest entries are pushed to the front of the queue | ||
| 266 | Frame* frame = present_queue.front(); | ||
| 267 | present_queue.pop_front(); | ||
| 268 | // remove all old entries from the present queue and move them back to the free_queue | ||
| 269 | for (auto f : present_queue) { | ||
| 270 | free_queue.push(f); | ||
| 271 | } | ||
| 272 | present_queue.clear(); | ||
| 273 | previous_frame = frame; | ||
| 274 | return frame; | ||
| 275 | } | ||
| 276 | }; | ||
| 277 | |||
| 278 | RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | 130 | RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, |
| 279 | Core::Frontend::EmuWindow& emu_window_, | 131 | Core::Frontend::EmuWindow& emu_window_, |
| 280 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, | 132 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, |
| 281 | std::unique_ptr<Core::Frontend::GraphicsContext> context) | 133 | std::unique_ptr<Core::Frontend::GraphicsContext> context) |
| 282 | : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_}, | 134 | : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_}, |
| 283 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device}, | 135 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {} |
| 284 | has_debug_tool{HasDebugTool()} {} | ||
| 285 | 136 | ||
| 286 | RendererOpenGL::~RendererOpenGL() = default; | 137 | RendererOpenGL::~RendererOpenGL() = default; |
| 287 | 138 | ||
| 288 | MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64)); | ||
| 289 | MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128)); | ||
| 290 | |||
| 291 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 139 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { |
| 292 | if (!framebuffer) { | 140 | if (!framebuffer) { |
| 293 | return; | 141 | return; |
| @@ -296,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 296 | PrepareRendertarget(framebuffer); | 144 | PrepareRendertarget(framebuffer); |
| 297 | RenderScreenshot(); | 145 | RenderScreenshot(); |
| 298 | 146 | ||
| 299 | Frame* frame; | 147 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| 300 | { | 148 | DrawScreen(emu_window.GetFramebufferLayout()); |
| 301 | MICROPROFILE_SCOPE(OpenGL_WaitPresent); | ||
| 302 | |||
| 303 | frame = frame_mailbox->GetRenderFrame(); | ||
| 304 | |||
| 305 | // Clean up sync objects before drawing | ||
| 306 | |||
| 307 | // INTEL driver workaround. We can't delete the previous render sync object until we are | ||
| 308 | // sure that the presentation is done | ||
| 309 | if (frame->present_fence) { | ||
| 310 | glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 311 | } | ||
| 312 | |||
| 313 | // delete the draw fence if the frame wasn't presented | ||
| 314 | if (frame->render_fence) { | ||
| 315 | glDeleteSync(frame->render_fence); | ||
| 316 | frame->render_fence = 0; | ||
| 317 | } | ||
| 318 | |||
| 319 | // wait for the presentation to be done | ||
| 320 | if (frame->present_fence) { | ||
| 321 | glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 322 | glDeleteSync(frame->present_fence); | ||
| 323 | frame->present_fence = 0; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | 149 | ||
| 327 | { | 150 | ++m_current_frame; |
| 328 | MICROPROFILE_SCOPE(OpenGL_RenderFrame); | ||
| 329 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 330 | 151 | ||
| 331 | // Recreate the frame if the size of the window has changed | 152 | rasterizer->TickFrame(); |
| 332 | if (layout.width != frame->width || layout.height != frame->height || | ||
| 333 | screen_info.display_srgb != frame->is_srgb) { | ||
| 334 | LOG_DEBUG(Render_OpenGL, "Reloading render frame"); | ||
| 335 | frame->is_srgb = screen_info.display_srgb; | ||
| 336 | frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height); | ||
| 337 | } | ||
| 338 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle); | ||
| 339 | DrawScreen(layout); | ||
| 340 | // Create a fence for the frontend to wait on and swap this frame to OffTex | ||
| 341 | frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||
| 342 | glFlush(); | ||
| 343 | frame_mailbox->ReleaseRenderFrame(frame); | ||
| 344 | m_current_frame++; | ||
| 345 | rasterizer->TickFrame(); | ||
| 346 | } | ||
| 347 | 153 | ||
| 348 | render_window.PollEvents(); | 154 | render_window.PollEvents(); |
| 349 | if (has_debug_tool) { | 155 | context->SwapBuffers(); |
| 350 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | ||
| 351 | Present(0); | ||
| 352 | context->SwapBuffers(); | ||
| 353 | } | ||
| 354 | } | 156 | } |
| 355 | 157 | ||
| 356 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | 158 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { |
| 357 | if (framebuffer) { | 159 | if (!framebuffer) { |
| 358 | // If framebuffer is provided, reload it from memory to a texture | 160 | return; |
| 359 | if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || | 161 | } |
| 360 | screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || | 162 | // If framebuffer is provided, reload it from memory to a texture |
| 361 | screen_info.texture.pixel_format != framebuffer->pixel_format || | 163 | if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || |
| 362 | gl_framebuffer_data.empty()) { | 164 | screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || |
| 363 | // Reallocate texture if the framebuffer size has changed. | 165 | screen_info.texture.pixel_format != framebuffer->pixel_format || |
| 364 | // This is expected to not happen very often and hence should not be a | 166 | gl_framebuffer_data.empty()) { |
| 365 | // performance problem. | 167 | // Reallocate texture if the framebuffer size has changed. |
| 366 | ConfigureFramebufferTexture(screen_info.texture, *framebuffer); | 168 | // This is expected to not happen very often and hence should not be a |
| 367 | } | 169 | // performance problem. |
| 368 | 170 | ConfigureFramebufferTexture(screen_info.texture, *framebuffer); | |
| 369 | // Load the framebuffer from memory, draw it to the screen, and swap buffers | ||
| 370 | LoadFBToScreenInfo(*framebuffer); | ||
| 371 | } | 171 | } |
| 172 | |||
| 173 | // Load the framebuffer from memory, draw it to the screen, and swap buffers | ||
| 174 | LoadFBToScreenInfo(*framebuffer); | ||
| 372 | } | 175 | } |
| 373 | 176 | ||
| 374 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | 177 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { |
| @@ -418,8 +221,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 418 | } | 221 | } |
| 419 | 222 | ||
| 420 | void RendererOpenGL::InitOpenGLObjects() { | 223 | void RendererOpenGL::InitOpenGLObjects() { |
| 421 | frame_mailbox = std::make_unique<FrameMailbox>(); | ||
| 422 | |||
| 423 | glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), | 224 | glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), |
| 424 | Settings::values.bg_blue.GetValue(), 0.0f); | 225 | Settings::values.bg_blue.GetValue(), 0.0f); |
| 425 | 226 | ||
| @@ -647,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 647 | program_manager.RestoreGuestPipeline(); | 448 | program_manager.RestoreGuestPipeline(); |
| 648 | } | 449 | } |
| 649 | 450 | ||
| 650 | bool RendererOpenGL::TryPresent(int timeout_ms) { | ||
| 651 | if (has_debug_tool) { | ||
| 652 | LOG_DEBUG(Render_OpenGL, | ||
| 653 | "Skipping presentation because we are presenting on the main context"); | ||
| 654 | return false; | ||
| 655 | } | ||
| 656 | return Present(timeout_ms); | ||
| 657 | } | ||
| 658 | |||
| 659 | bool RendererOpenGL::Present(int timeout_ms) { | ||
| 660 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 661 | auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms); | ||
| 662 | if (!frame) { | ||
| 663 | LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); | ||
| 664 | return false; | ||
| 665 | } | ||
| 666 | |||
| 667 | // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a | ||
| 668 | // readback since we won't be doing any blending | ||
| 669 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 670 | |||
| 671 | // Recreate the presentation FBO if the color attachment was changed | ||
| 672 | if (frame->color_reloaded) { | ||
| 673 | LOG_DEBUG(Render_OpenGL, "Reloading present frame"); | ||
| 674 | frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height); | ||
| 675 | } | ||
| 676 | glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED); | ||
| 677 | // INTEL workaround. | ||
| 678 | // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete | ||
| 679 | // it on the emulation thread without too much penalty | ||
| 680 | // glDeleteSync(frame.render_sync); | ||
| 681 | // frame.render_sync = 0; | ||
| 682 | |||
| 683 | glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle); | ||
| 684 | glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height, | ||
| 685 | GL_COLOR_BUFFER_BIT, GL_LINEAR); | ||
| 686 | |||
| 687 | // Insert fence for the main thread to block on | ||
| 688 | frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); | ||
| 689 | glFlush(); | ||
| 690 | |||
| 691 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); | ||
| 692 | return true; | ||
| 693 | } | ||
| 694 | |||
| 695 | void RendererOpenGL::RenderScreenshot() { | 451 | void RendererOpenGL::RenderScreenshot() { |
| 696 | if (!renderer_settings.screenshot_requested) { | 452 | if (!renderer_settings.screenshot_requested) { |
| 697 | return; | 453 | return; |
| @@ -706,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() { | |||
| 706 | screenshot_framebuffer.Create(); | 462 | screenshot_framebuffer.Create(); |
| 707 | glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); | 463 | glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle); |
| 708 | 464 | ||
| 709 | Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | 465 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; |
| 710 | 466 | ||
| 711 | GLuint renderbuffer; | 467 | GLuint renderbuffer; |
| 712 | glGenRenderbuffers(1, &renderbuffer); | 468 | glGenRenderbuffers(1, &renderbuffer); |
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 5329577fb..9ef181f95 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -55,14 +55,6 @@ struct ScreenInfo { | |||
| 55 | TextureInfo texture; | 55 | TextureInfo texture; |
| 56 | }; | 56 | }; |
| 57 | 57 | ||
| 58 | struct PresentationTexture { | ||
| 59 | u32 width = 0; | ||
| 60 | u32 height = 0; | ||
| 61 | OGLTexture texture; | ||
| 62 | }; | ||
| 63 | |||
| 64 | class FrameMailbox; | ||
| 65 | |||
| 66 | class RendererOpenGL final : public VideoCore::RendererBase { | 58 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 67 | public: | 59 | public: |
| 68 | explicit RendererOpenGL(Core::TelemetrySession& telemetry_session, | 60 | explicit RendererOpenGL(Core::TelemetrySession& telemetry_session, |
| @@ -74,7 +66,6 @@ public: | |||
| 74 | bool Init() override; | 66 | bool Init() override; |
| 75 | void ShutDown() override; | 67 | void ShutDown() override; |
| 76 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 68 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 77 | bool TryPresent(int timeout_ms) override; | ||
| 78 | 69 | ||
| 79 | private: | 70 | private: |
| 80 | /// Initializes the OpenGL state and creates persistent objects. | 71 | /// Initializes the OpenGL state and creates persistent objects. |
| @@ -102,8 +93,6 @@ private: | |||
| 102 | 93 | ||
| 103 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); | 94 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); |
| 104 | 95 | ||
| 105 | bool Present(int timeout_ms); | ||
| 106 | |||
| 107 | Core::TelemetrySession& telemetry_session; | 96 | Core::TelemetrySession& telemetry_session; |
| 108 | Core::Frontend::EmuWindow& emu_window; | 97 | Core::Frontend::EmuWindow& emu_window; |
| 109 | Core::Memory::Memory& cpu_memory; | 98 | Core::Memory::Memory& cpu_memory; |
| @@ -134,11 +123,6 @@ private: | |||
| 134 | /// Used for transforming the framebuffer orientation | 123 | /// Used for transforming the framebuffer orientation |
| 135 | Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; | 124 | Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{}; |
| 136 | Common::Rectangle<int> framebuffer_crop_rect; | 125 | Common::Rectangle<int> framebuffer_crop_rect; |
| 137 | |||
| 138 | /// Frame presentation mailbox | ||
| 139 | std::unique_ptr<FrameMailbox> frame_mailbox; | ||
| 140 | |||
| 141 | bool has_debug_tool = false; | ||
| 142 | }; | 126 | }; |
| 143 | 127 | ||
| 144 | } // namespace OpenGL | 128 | } // namespace OpenGL |
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 | ||
| 86 | VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) { | 87 | VkCompareOp 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 | ||
| 306 | VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) { | 308 | VkFormat 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/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 0e4583986..d38e797a4 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -283,11 +283,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 283 | render_window.PollEvents(); | 283 | render_window.PollEvents(); |
| 284 | } | 284 | } |
| 285 | 285 | ||
| 286 | bool RendererVulkan::TryPresent(int /*timeout_ms*/) { | ||
| 287 | // TODO (bunnei): ImplementMe | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | bool RendererVulkan::Init() { | 286 | bool RendererVulkan::Init() { |
| 292 | library = OpenVulkanLibrary(); | 287 | library = OpenVulkanLibrary(); |
| 293 | instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, | 288 | instance = CreateInstance(library, dld, render_window.GetWindowInfo().type, |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index ddff77942..5085310d0 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h | |||
| @@ -55,7 +55,6 @@ public: | |||
| 55 | bool Init() override; | 55 | bool Init() override; |
| 56 | void ShutDown() override; | 56 | void ShutDown() override; |
| 57 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 57 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; |
| 58 | bool TryPresent(int timeout_ms) override; | ||
| 59 | 58 | ||
| 60 | static std::vector<std::string> EnumerateDevices(); | 59 | static std::vector<std::string> EnumerateDevices(); |
| 61 | 60 | ||
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 | ||
| 228 | std::size_t GetImageComponentMask(TextureFormat format) { | 231 | std::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 | ||
| 265 | std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) { | 269 | std::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 | ||
| 764 | Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { | 764 | Node4 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/bootmanager.cpp b/src/yuzu/bootmanager.cpp index caa2d06d3..408eac2b7 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -218,15 +218,6 @@ public: | |||
| 218 | 218 | ||
| 219 | virtual ~RenderWidget() = default; | 219 | virtual ~RenderWidget() = default; |
| 220 | 220 | ||
| 221 | /// Called on the UI thread when this Widget is ready to draw | ||
| 222 | /// Dervied classes can override this to draw the latest frame. | ||
| 223 | virtual void Present() {} | ||
| 224 | |||
| 225 | void paintEvent(QPaintEvent* event) override { | ||
| 226 | Present(); | ||
| 227 | update(); | ||
| 228 | } | ||
| 229 | |||
| 230 | QPaintEngine* paintEngine() const override { | 221 | QPaintEngine* paintEngine() const override { |
| 231 | return nullptr; | 222 | return nullptr; |
| 232 | } | 223 | } |
| @@ -245,20 +236,8 @@ public: | |||
| 245 | context = std::move(context_); | 236 | context = std::move(context_); |
| 246 | } | 237 | } |
| 247 | 238 | ||
| 248 | void Present() override { | ||
| 249 | if (!isVisible()) { | ||
| 250 | return; | ||
| 251 | } | ||
| 252 | |||
| 253 | context->MakeCurrent(); | ||
| 254 | if (Core::System::GetInstance().Renderer().TryPresent(100)) { | ||
| 255 | context->SwapBuffers(); | ||
| 256 | glFinish(); | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | private: | 239 | private: |
| 261 | std::unique_ptr<Core::Frontend::GraphicsContext> context{}; | 240 | std::unique_ptr<Core::Frontend::GraphicsContext> context; |
| 262 | }; | 241 | }; |
| 263 | 242 | ||
| 264 | #ifdef HAS_VULKAN | 243 | #ifdef HAS_VULKAN |
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 | ||
| 39 | const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = { | ||
| 40 | Qt::Key_7, | ||
| 41 | Qt::Key_8, | ||
| 42 | }; | ||
| 43 | |||
| 39 | const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ | 44 | const 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 | ||
| 177 | void ConfigureInput::changeEvent(QEvent* event) { | 182 | void 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 | ||
| 196 | void ConfigureInput::LoadPlayerControllerIndices() { | 202 | void 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 | ||
| 222 | void ConfigureInput::UpdateDockedState(bool is_handheld) { | 229 | void 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() { | |||
| 552 | void ConfigureInputPlayer::ClearAll() { | 653 | void 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() { | |||
| 659 | void ConfigureInputPlayer::HandleClick( | 771 | void 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 | ||
| 957 | void 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 | |||
| 835 | void ConfigureInputPlayer::showEvent(QShowEvent* event) { | 988 | void 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_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index a804d5185..521209622 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -13,9 +13,8 @@ | |||
| 13 | #include "input_common/sdl/sdl.h" | 13 | #include "input_common/sdl/sdl.h" |
| 14 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 14 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 15 | 15 | ||
| 16 | EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen, | 16 | EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_) |
| 17 | InputCommon::InputSubsystem* input_subsystem_) | 17 | : input_subsystem{input_subsystem_} { |
| 18 | : system{system}, input_subsystem{input_subsystem_} { | ||
| 19 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { | 18 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { |
| 20 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); | 19 | LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); |
| 21 | exit(1); | 20 | exit(1); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 82750ffec..53d756c3c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -20,8 +20,7 @@ class InputSubsystem; | |||
| 20 | 20 | ||
| 21 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { | 21 | class EmuWindow_SDL2 : public Core::Frontend::EmuWindow { |
| 22 | public: | 22 | public: |
| 23 | explicit EmuWindow_SDL2(Core::System& system, bool fullscreen, | 23 | explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem); |
| 24 | InputCommon::InputSubsystem* input_subsystem); | ||
| 25 | ~EmuWindow_SDL2(); | 24 | ~EmuWindow_SDL2(); |
| 26 | 25 | ||
| 27 | /// Polls window events | 26 | /// Polls window events |
| @@ -33,9 +32,6 @@ public: | |||
| 33 | /// Returns if window is shown (not minimized) | 32 | /// Returns if window is shown (not minimized) |
| 34 | bool IsShown() const override; | 33 | bool IsShown() const override; |
| 35 | 34 | ||
| 36 | /// Presents the next frame | ||
| 37 | virtual void Present() = 0; | ||
| 38 | |||
| 39 | protected: | 35 | protected: |
| 40 | /// Called by PollEvents when a key is pressed or released. | 36 | /// Called by PollEvents when a key is pressed or released. |
| 41 | void OnKeyEvent(int key, u8 state); | 37 | void OnKeyEvent(int key, u8 state); |
| @@ -67,9 +63,6 @@ protected: | |||
| 67 | /// Called when a configuration change affects the minimal size of the window | 63 | /// Called when a configuration change affects the minimal size of the window |
| 68 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; | 64 | void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override; |
| 69 | 65 | ||
| 70 | /// Instance of the system, used to access renderer for the presentation thread | ||
| 71 | Core::System& system; | ||
| 72 | |||
| 73 | /// Is the window still open? | 66 | /// Is the window still open? |
| 74 | bool is_open = true; | 67 | bool is_open = true; |
| 75 | 68 | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 881b67a76..5f35233b5 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -87,9 +87,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { | |||
| 87 | return unsupported_ext.empty(); | 87 | return unsupported_ext.empty(); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, | 90 | EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen) |
| 91 | InputCommon::InputSubsystem* input_subsystem) | 91 | : EmuWindow_SDL2{input_subsystem} { |
| 92 | : EmuWindow_SDL2{system, fullscreen, input_subsystem} { | ||
| 93 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); | 92 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); |
| 94 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); | 93 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); |
| 95 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); | 94 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); |
| @@ -163,13 +162,3 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() { | |||
| 163 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 162 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 164 | return std::make_unique<SDLGLContext>(); | 163 | return std::make_unique<SDLGLContext>(); |
| 165 | } | 164 | } |
| 166 | |||
| 167 | void EmuWindow_SDL2_GL::Present() { | ||
| 168 | SDL_GL_MakeCurrent(render_window, window_context); | ||
| 169 | SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0); | ||
| 170 | while (IsOpen()) { | ||
| 171 | system.Renderer().TryPresent(100); | ||
| 172 | SDL_GL_SwapWindow(render_window); | ||
| 173 | } | ||
| 174 | SDL_GL_MakeCurrent(render_window, nullptr); | ||
| 175 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index 732a64edd..dba5c293c 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -14,12 +14,9 @@ class InputSubsystem; | |||
| 14 | 14 | ||
| 15 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { | 15 | class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 { |
| 16 | public: | 16 | public: |
| 17 | explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen, | 17 | explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen); |
| 18 | InputCommon::InputSubsystem* input_subsystem); | ||
| 19 | ~EmuWindow_SDL2_GL(); | 18 | ~EmuWindow_SDL2_GL(); |
| 20 | 19 | ||
| 21 | void Present() override; | ||
| 22 | |||
| 23 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 20 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 24 | 21 | ||
| 25 | private: | 22 | private: |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index 53491f86e..3ba657c00 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -19,9 +19,8 @@ | |||
| 19 | #include <SDL.h> | 19 | #include <SDL.h> |
| 20 | #include <SDL_syswm.h> | 20 | #include <SDL_syswm.h> |
| 21 | 21 | ||
| 22 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, | 22 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem) |
| 23 | InputCommon::InputSubsystem* input_subsystem) | 23 | : EmuWindow_SDL2{input_subsystem} { |
| 24 | : EmuWindow_SDL2{system, fullscreen, input_subsystem} { | ||
| 25 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, | 24 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, |
| 26 | Common::g_scm_branch, Common::g_scm_desc); | 25 | Common::g_scm_branch, Common::g_scm_desc); |
| 27 | render_window = | 26 | render_window = |
| @@ -74,7 +73,3 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default; | |||
| 74 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { | 73 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { |
| 75 | return std::make_unique<DummyContext>(); | 74 | return std::make_unique<DummyContext>(); |
| 76 | } | 75 | } |
| 77 | |||
| 78 | void EmuWindow_SDL2_VK::Present() { | ||
| 79 | // TODO (bunnei): ImplementMe | ||
| 80 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index f99704d4c..bdfdc3c6f 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -19,11 +19,8 @@ class InputSubsystem; | |||
| 19 | 19 | ||
| 20 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | 20 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { |
| 21 | public: | 21 | public: |
| 22 | explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen, | 22 | explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem); |
| 23 | InputCommon::InputSubsystem* input_subsystem); | 23 | ~EmuWindow_SDL2_VK() override; |
| 24 | ~EmuWindow_SDL2_VK(); | ||
| 25 | |||
| 26 | void Present() override; | ||
| 27 | 24 | ||
| 28 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 25 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 29 | }; | 26 | }; |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e960b5413..3a76c785f 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -185,11 +185,11 @@ int main(int argc, char** argv) { | |||
| 185 | std::unique_ptr<EmuWindow_SDL2> emu_window; | 185 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 186 | switch (Settings::values.renderer_backend.GetValue()) { | 186 | switch (Settings::values.renderer_backend.GetValue()) { |
| 187 | case Settings::RendererBackend::OpenGL: | 187 | case Settings::RendererBackend::OpenGL: |
| 188 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen, &input_subsystem); | 188 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen); |
| 189 | break; | 189 | break; |
| 190 | case Settings::RendererBackend::Vulkan: | 190 | case Settings::RendererBackend::Vulkan: |
| 191 | #ifdef HAS_VULKAN | 191 | #ifdef HAS_VULKAN |
| 192 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen, &input_subsystem); | 192 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem); |
| 193 | break; | 193 | break; |
| 194 | #else | 194 | #else |
| 195 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); | 195 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); |
| @@ -240,14 +240,11 @@ int main(int argc, char** argv) { | |||
| 240 | system.CurrentProcess()->GetTitleID(), false, | 240 | system.CurrentProcess()->GetTitleID(), false, |
| 241 | [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); | 241 | [](VideoCore::LoadCallbackStage, size_t value, size_t total) {}); |
| 242 | 242 | ||
| 243 | std::thread render_thread([&emu_window] { emu_window->Present(); }); | ||
| 244 | system.Run(); | 243 | system.Run(); |
| 245 | while (emu_window->IsOpen()) { | 244 | while (emu_window->IsOpen()) { |
| 246 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | 245 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| 247 | } | 246 | } |
| 248 | system.Pause(); | 247 | system.Pause(); |
| 249 | render_thread.join(); | ||
| 250 | |||
| 251 | system.Shutdown(); | 248 | system.Shutdown(); |
| 252 | 249 | ||
| 253 | detached_tasks.WaitForAllTasks(); | 250 | detached_tasks.WaitForAllTasks(); |
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; |