diff options
Diffstat (limited to 'src/core')
147 files changed, 3915 insertions, 1560 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c85c9485f..b6dc25f6b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,9 +1,3 @@ -if (YUZU_ENABLE_BOXCAT) - set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h) -else() - set(BCAT_BOXCAT_ADDITIONAL_SOURCES) -endif() - add_library(core STATIC arm/arm_interface.h arm/arm_interface.cpp @@ -126,6 +120,8 @@ add_library(core STATIC file_sys/vfs_vector.h file_sys/xts_archive.cpp file_sys/xts_archive.h + frontend/applets/controller.cpp + frontend/applets/controller.h frontend/applets/error.cpp frontend/applets/error.h frontend/applets/general_frontend.cpp @@ -244,6 +240,8 @@ add_library(core STATIC hle/service/am/applet_oe.h hle/service/am/applets/applets.cpp hle/service/am/applets/applets.h + hle/service/am/applets/controller.cpp + hle/service/am/applets/controller.h hle/service/am/applets/error.cpp hle/service/am/applets/error.h hle/service/am/applets/general_backend.cpp @@ -299,7 +297,6 @@ add_library(core STATIC hle/service/audio/hwopus.h hle/service/bcat/backend/backend.cpp hle/service/bcat/backend/backend.h - ${BCAT_BOXCAT_ADDITIONAL_SOURCES} hle/service/bcat/bcat.cpp hle/service/bcat/bcat.h hle/service/bcat/module.cpp @@ -491,6 +488,7 @@ add_library(core STATIC hle/service/sm/controller.h hle/service/sm/sm.cpp hle/service/sm/sm.h + hle/service/sockets/blocking_worker.h hle/service/sockets/bsd.cpp hle/service/sockets/bsd.h hle/service/sockets/ethc.cpp @@ -501,6 +499,8 @@ add_library(core STATIC hle/service/sockets/sfdnsres.h hle/service/sockets/sockets.cpp hle/service/sockets/sockets.h + hle/service/sockets/sockets_translate.cpp + hle/service/sockets/sockets_translate.h hle/service/spl/csrng.cpp hle/service/spl/csrng.h hle/service/spl/module.cpp @@ -601,6 +601,13 @@ add_library(core STATIC tools/freezer.h ) +if (YUZU_ENABLE_BOXCAT) + target_sources(core PRIVATE + hle/service/bcat/backend/boxcat.cpp + hle/service/bcat/backend/boxcat.h + ) +endif() + if (MSVC) target_compile_options(core PRIVATE # 'expression' : signed/unsigned mismatch @@ -616,6 +623,17 @@ if (MSVC) # 'context' : truncation from 'type1' to 'type2' /we4305 ) +else() + target_compile_options(core PRIVATE + -Werror=conversion + -Werror=ignored-qualifiers + -Werror=implicit-fallthrough + -Werror=reorder + -Werror=sign-compare + -Werror=unused-but-set-parameter + -Werror=unused-but-set-variable + -Werror=unused-variable + ) endif() create_target_directory_groups(core) diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp index df0350881..9c8898700 100644 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ b/src/core/arm/cpu_interrupt_handler.cpp @@ -7,9 +7,7 @@ namespace Core { -CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { - interrupt_event = std::make_unique<Common::Event>(); -} +CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {} CPUInterruptHandler::~CPUInterruptHandler() = default; @@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { if (is_interrupted_) { interrupt_event->Set(); } - this->is_interrupted = is_interrupted_; + is_interrupted = is_interrupted_; } void CPUInterruptHandler::AwaitInterrupt() { diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h index 3d062d326..71e582f79 100644 --- a/src/core/arm/cpu_interrupt_handler.h +++ b/src/core/arm/cpu_interrupt_handler.h @@ -4,6 +4,7 @@ #pragma once +#include <atomic> #include <memory> namespace Common { @@ -32,8 +33,8 @@ public: void AwaitInterrupt(); private: - bool is_interrupted{}; std::unique_ptr<Common::Event> interrupt_event; + std::atomic_bool is_interrupted{false}; }; } // namespace Core diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 443ca72eb..b5f28a86e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& config.wall_clock_cntpct = uses_wall_clock; // Safe optimizations - if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& } } + // Unsafe optimizations + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + } + return std::make_unique<Dynarmic::A32::Jit>(config); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a63a04a25..ce9968724 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& config.wall_clock_cntpct = uses_wall_clock; // Safe optimizations - if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& } } + // Unsafe optimizations + if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { + config.unsafe_optimizations = true; + if (Settings::values.cpuopt_unsafe_unfuse_fma) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + } + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; + } + } + return std::make_shared<Dynarmic::A64::Jit>(config); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 54556e0f9..caefc09f4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne CoprocReg CRm, unsigned opc2) { LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, CRm, opc2); - return {}; + return std::nullopt; } CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, @@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", long_transfer ? "l" : "", CRd); } - return {}; + return std::nullopt; } std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, @@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", long_transfer ? "l" : "", CRd); } - return {}; + return std::nullopt; } } // namespace Core 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: std::optional<u8> option) override; ARM_Dynarmic_32& parent; - u32 uprw; - u32 uro; + u32 uprw = 0; + u32 uro = 0; }; } // namespace Core diff --git a/src/core/core.cpp b/src/core/core.cpp index c2c0eec0b..81e8cc338 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -178,7 +178,7 @@ struct System::Impl { arp_manager.ResetAll(); telemetry_session = std::make_unique<Core::TelemetrySession>(); - service_manager = std::make_shared<Service::SM::ServiceManager>(); + service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); Service::Init(service_manager, system); GDBStub::DeferStart(); @@ -188,7 +188,6 @@ struct System::Impl { if (!gpu_core) { return ResultStatus::ErrorVideoCore; } - gpu_core->Renderer().Rasterizer().SetupDirtyFlags(); is_powered_on = true; exit_lock = false; @@ -222,7 +221,7 @@ struct System::Impl { telemetry_session->AddInitialInfo(*app_loader); auto main_process = Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); - const auto [load_result, load_parameters] = app_loader->Load(*main_process); + const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); if (load_result != Loader::ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); Shutdown(); @@ -630,11 +629,11 @@ Loader::AppLoader& System::GetAppLoader() const { return *impl->app_loader; } -void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { +void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { impl->virtual_filesystem = std::move(vfs); } -std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { +FileSys::VirtualFilesystem System::GetFilesystem() const { return impl->virtual_filesystem; } diff --git a/src/core/core.h b/src/core/core.h index 5c6cfbffe..27efe30bb 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -120,7 +120,7 @@ public: * Gets the instance of the System singleton class. * @returns Reference to the instance of the System singleton class. */ - static System& GetInstance() { + [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { return s_instance; } @@ -316,9 +316,9 @@ public: Service::SM::ServiceManager& ServiceManager(); const Service::SM::ServiceManager& ServiceManager() const; - void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); + void SetFilesystem(FileSys::VirtualFilesystem vfs); - std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; + FileSys::VirtualFilesystem GetFilesystem() const; void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, const std::array<u8, 0x20>& build_id, VAddr main_region_begin, diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 71af26ec5..e6c8461a5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -7,14 +7,14 @@ #include <string> #include <tuple> -#include "common/assert.h" #include "common/microprofile.h" #include "core/core_timing.h" #include "core/core_timing_util.h" +#include "core/hardware_properties.h" namespace Core::Timing { -constexpr u64 MAX_SLICE_LENGTH = 4000; +constexpr s64 MAX_SLICE_LENGTH = 4000; std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { return std::make_shared<EventType>(std::move(callback), std::move(name)); @@ -37,10 +37,8 @@ struct CoreTiming::Event { } }; -CoreTiming::CoreTiming() { - clock = - Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); -} +CoreTiming::CoreTiming() + : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} CoreTiming::~CoreTiming() = default; @@ -136,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, void CoreTiming::AddTicks(u64 ticks) { this->ticks += ticks; - downcount -= ticks; + downcount -= static_cast<s64>(ticks); } void CoreTiming::Idle() { diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index aefc63663..8ce8e602e 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp @@ -8,6 +8,7 @@ #include <limits> #include "common/logging/log.h" #include "common/uint128.h" +#include "core/hardware_properties.h" namespace Core::Timing { diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 2ed979e14..e4a046bf9 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h @@ -6,7 +6,6 @@ #include <chrono> #include "common/common_types.h" -#include "core/hardware_properties.h" namespace Core::Timing { diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 358943429..688b99eba 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -41,9 +41,9 @@ void CpuManager::Shutdown() { running_mode = false; Pause(false); if (is_multicore) { - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread->join(); - core_data[core].host_thread.reset(); + for (auto& data : core_data) { + data.host_thread->join(); + data.host_thread.reset(); } } else { core_data[0].host_thread->join(); @@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) { bool all_not_barrier = false; while (!all_not_barrier) { all_not_barrier = true; - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - all_not_barrier &= - !core_data[core].is_running.load() && core_data[core].initialized.load(); + for (const auto& data : core_data) { + all_not_barrier &= !data.is_running.load() && data.initialized.load(); } } - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].enter_barrier->Set(); + for (auto& data : core_data) { + data.enter_barrier->Set(); } if (paused_state.load()) { bool all_barrier = false; while (!all_barrier) { all_barrier = true; - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - all_barrier &= - core_data[core].is_paused.load() && core_data[core].initialized.load(); + for (const auto& data : core_data) { + all_barrier &= data.is_paused.load() && data.initialized.load(); } } - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].exit_barrier->Set(); + for (auto& data : core_data) { + data.exit_barrier->Set(); } } } else { @@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) { bool all_barrier = false; while (!all_barrier) { all_barrier = true; - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - all_barrier &= - core_data[core].is_paused.load() && core_data[core].initialized.load(); + for (const auto& data : core_data) { + all_barrier &= data.is_paused.load() && data.initialized.load(); } } /// Don't release the barrier @@ -331,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) { system.RegisterCoreThread(core); std::string name; if (is_multicore) { - name = "yuzu:CoreCPUThread_" + std::to_string(core); + name = "yuzu:CPUCore_" + std::to_string(core); } else { name = "yuzu:CPUThread"; } diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 8783d1ac2..da15f764a 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -23,7 +23,6 @@ #include "common/hex_util.h" #include "common/logging/log.h" #include "common/string_util.h" -#include "core/core.h" #include "core/crypto/aes_util.h" #include "core/crypto/key_manager.h" #include "core/crypto/partition_data_manager.h" @@ -36,6 +35,7 @@ #include "core/settings.h" namespace Core::Crypto { +namespace { constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; constexpr u64 FULL_TICKET_SIZE = 0x400; @@ -49,7 +49,72 @@ constexpr std::array eticket_source_hashes{ }; // clang-format on -const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ +constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{ + {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, + {"eticket_rsa_kek_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, + {"eticket_rsa_kekek_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, + {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, + {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, + {"rsa_oaep_kek_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, + {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, + {"aes_kek_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, + {"aes_key_generation_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, + {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, + {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, + {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, + {"key_area_key_application_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_ocean_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_system_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), + static_cast<u64>(KeyAreaKeyType::System)}}, + {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, + {"keyblob_mac_key_source", + {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, + {"tsec_key", {S128KeyType::TSEC, 0, 0}}, + {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, + {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, + {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, + {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, + {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, + {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, + {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, +}}; + +auto Find128ByName(std::string_view name) { + return std::find_if(s128_file_id.begin(), s128_file_id.end(), + [&name](const auto& pair) { return pair.first == name; }); +} + +constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{ + {"header_key", {S256KeyType::Header, 0, 0}}, + {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, + {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, + {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, + {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, + {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, +}}; + +auto Find256ByName(std::string_view name) { + return std::find_if(s256_file_id.begin(), s256_file_id.end(), + [&name](const auto& pair) { return pair.first == name; }); +} + +using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>; +constexpr KeyArray KEYS_VARIABLE_LENGTH{{ {{S128KeyType::Master, 0}, "master_key_"}, {{S128KeyType::Package1, 0}, "package1_key_"}, {{S128KeyType::Package2, 0}, "package2_key_"}, @@ -57,14 +122,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, -}; +}}; -namespace { template <std::size_t Size> bool IsAllZeroArray(const std::array<u8, Size>& array) { return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); } -} // namespace +} // Anonymous namespace u64 GetSignatureTypeDataSize(SignatureType type) { switch (type) { @@ -347,7 +411,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke // Combine sources and seed for (auto& source : sd_key_sources) { for (std::size_t i = 0; i < source.size(); ++i) { - source[i] ^= sd_seed[i & 0xF]; + source[i] = static_cast<u8>(source[i] ^ sd_seed[i & 0xF]); } } @@ -564,13 +628,13 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; } else { out[0] = Common::ToLower(out[0]); - if (s128_file_id.find(out[0]) != s128_file_id.end()) { - const auto index = s128_file_id.at(out[0]); - Key128 key = Common::HexStringToArray<16>(out[1]); + if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) { + const auto& index = iter128->second; + const Key128 key = Common::HexStringToArray<16>(out[1]); s128_keys[{index.type, index.field1, index.field2}] = key; - } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { - const auto index = s256_file_id.at(out[0]); - Key256 key = Common::HexStringToArray<32>(out[1]); + } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) { + const auto& index = iter256->second; + const Key256 key = Common::HexStringToArray<32>(out[1]); s256_keys[{index.type, index.field1, index.field2}] = key; } else if (out[0].compare(0, 8, "keyblob_") == 0 && out[0].compare(0, 9, "keyblob_k") != 0) { @@ -742,8 +806,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { } const auto iter2 = std::find_if( - s128_file_id.begin(), s128_file_id.end(), - [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { + s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) { return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == std::tie(id, field1, field2); }); @@ -753,9 +816,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { // Variable cases if (id == S128KeyType::KeyArea) { - static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", - "key_area_key_ocean_{:02X}", - "key_area_key_system_{:02X}"}; + static constexpr std::array<const char*, 3> kak_names = { + "key_area_key_application_{:02X}", + "key_area_key_ocean_{:02X}", + "key_area_key_system_{:02X}", + }; WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); } else if (id == S128KeyType::Master) { WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); @@ -781,8 +846,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { return; } const auto iter = std::find_if( - s256_file_id.begin(), s256_file_id.end(), - [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { + s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) { return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == std::tie(id, field1, field2); }); @@ -957,10 +1021,10 @@ void KeyManager::DeriveBase() { } } -void KeyManager::DeriveETicket(PartitionDataManager& data) { +void KeyManager::DeriveETicket(PartitionDataManager& data, + const FileSys::ContentProvider& provider) { // ETicket keys - const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( - 0x0100000000000033, FileSys::ContentRecordType::Program); + const auto es = provider.GetEntry(0x0100000000000033, FileSys::ContentRecordType::Program); if (es == nullptr) { return; @@ -1245,58 +1309,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) { SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); return true; } - -const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { - {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, - {"eticket_rsa_kek_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, - {"eticket_rsa_kekek_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, - {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, - {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, - {"rsa_oaep_kek_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, - {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, - {"aes_kek_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, - {"aes_key_generation_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, - {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, - {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, - {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, - {"key_area_key_application_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::Application)}}, - {"key_area_key_ocean_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::Ocean)}}, - {"key_area_key_system_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), - static_cast<u64>(KeyAreaKeyType::System)}}, - {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, - {"keyblob_mac_key_source", - {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, - {"tsec_key", {S128KeyType::TSEC, 0, 0}}, - {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, - {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, - {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, - {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, - {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, - {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, - {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, -}; - -const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { - {"header_key", {S256KeyType::Header, 0, 0}}, - {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, - {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, - {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, - {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, - {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, -}; } // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index bdca3770a..0a7220286 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -10,7 +10,6 @@ #include <string> #include <variant> -#include <boost/container/flat_map.hpp> #include <fmt/format.h> #include "common/common_funcs.h" #include "common/common_types.h" @@ -21,6 +20,10 @@ namespace Common::FS { class IOFile; } +namespace FileSys { +class ContentProvider; +} + namespace Loader { enum class ResultStatus : u16; } @@ -253,7 +256,7 @@ public: bool BaseDeriveNecessary() const; void DeriveBase(); - void DeriveETicket(PartitionDataManager& data); + void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider); void PopulateTickets(); void SynthesizeTickets(); @@ -293,9 +296,6 @@ private: void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); - - static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; - static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; }; Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 46136d04a..5f1c86a09 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp @@ -26,6 +26,7 @@ #include "core/file_sys/vfs.h" #include "core/file_sys/vfs_offset.h" #include "core/file_sys/vfs_vector.h" +#include "core/loader/loader.h" using Common::AsArray; diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 9ffda2e14..7c6304ff0 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp @@ -4,11 +4,10 @@ #include <fmt/format.h> #include "common/file_util.h" -#include "core/core.h" #include "core/file_sys/bis_factory.h" #include "core/file_sys/mode.h" #include "core/file_sys/registered_cache.h" -#include "core/settings.h" +#include "core/file_sys/vfs.h" namespace FileSys { @@ -82,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { } } -VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { +VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id, + VirtualFilesystem file_system) const { auto& keys = Core::Crypto::KeyManager::Instance(); - Core::Crypto::PartitionDataManager pdm{ - Core::System::GetInstance().GetFilesystem()->OpenDirectory( - Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)}; + Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( + Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)}; keys.PopulateFromPartitionData(pdm); switch (id) { diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 8f0451c98..136485881 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h @@ -6,7 +6,8 @@ #include <memory> -#include "core/file_sys/vfs.h" +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" namespace FileSys { @@ -51,7 +52,7 @@ public: VirtualDir GetModificationDumpRoot(u64 title_id) const; VirtualDir OpenPartition(BisPartitionId id) const; - VirtualFile OpenPartitionStorage(BisPartitionId id) const; + VirtualFile OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const; VirtualDir GetImageDirectory() const; diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 664a47e7f..956da68f7 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -8,11 +8,11 @@ #include <fmt/ostream.h> #include "common/logging/log.h" +#include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/romfs.h" #include "core/file_sys/submission_package.h" #include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_offset.h" @@ -31,7 +31,8 @@ constexpr std::array partition_names{ XCI::XCI(VirtualFile file_) : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, - partitions(partition_names.size()), partitions_raw(partition_names.size()) { + partitions(partition_names.size()), + partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { if (file->ReadObject(&header) != sizeof(GamecardHeader)) { status = Loader::ResultStatus::ErrorBadXCIHeader; return; diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index e1b136426..2d0a0f285 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -9,9 +9,12 @@ #include <vector> #include "common/common_types.h" #include "common/swap.h" -#include "core/crypto/key_manager.h" #include "core/file_sys/vfs.h" +namespace Core::Crypto { +class KeyManager; +} + namespace Loader { enum class ResultStatus : u16; } @@ -140,6 +143,6 @@ private: u64 update_normal_partition_end; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); + Core::Crypto::KeyManager& keys; }; } // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 5039341c7..76af47ff9 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -10,10 +10,10 @@ #include "common/logging/log.h" #include "core/crypto/aes_util.h" #include "core/crypto/ctr_encryption_layer.h" +#include "core/crypto/key_manager.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_patch.h" #include "core/file_sys/partition_filesystem.h" -#include "core/file_sys/romfs.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" @@ -119,7 +119,8 @@ static bool IsValidNCA(const NCAHeader& header) { } NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) - : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { + : file(std::move(file_)), + bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} { if (file == nullptr) { status = Loader::ResultStatus::ErrorNullFile; return; @@ -322,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); subsection_buckets.back().entries.push_back({size, {0}, 0}); - std::optional<Core::Crypto::Key128> key = {}; + std::optional<Core::Crypto::Key128> key; if (encrypted) { if (has_rights_id) { status = Loader::ResultStatus::Success; @@ -441,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() { memcpy(rights_id.data(), header.rights_id.data(), 16); if (rights_id == u128{}) { status = Loader::ResultStatus::ErrorInvalidRightsID; - return {}; + return std::nullopt; } auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); if (titlekey == Core::Crypto::Key128{}) { status = Loader::ResultStatus::ErrorMissingTitlekey; - return {}; + return std::nullopt; } if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { status = Loader::ResultStatus::ErrorMissingTitlekek; - return {}; + return std::nullopt; } Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( @@ -476,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s case NCASectionCryptoType::BKTR: LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); { - std::optional<Core::Crypto::Key128> key = {}; + std::optional<Core::Crypto::Key128> key; if (has_rights_id) { status = Loader::ResultStatus::Success; key = GetTitlekey(); diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index d25cbcf91..69292232a 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -158,7 +158,7 @@ private: bool encrypted = false; bool is_update = false; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); + Core::Crypto::KeyManager& keys; }; } // namespace FileSys diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 63cd2eead..b0a130345 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -5,6 +5,7 @@ #include "common/string_util.h" #include "common/swap.h" #include "core/file_sys/control_metadata.h" +#include "core/file_sys/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index e37b2fadf..403c4219a 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" namespace FileSys { @@ -83,7 +83,7 @@ enum class Language : u8 { Italian = 7, Dutch = 8, CanadianFrench = 9, - Portugese = 10, + Portuguese = 10, Russian = 11, Korean = 12, Taiwanese = 13, diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp index 2aff2708a..c52fafb6f 100644 --- a/src/core/file_sys/fsmitm_romfsbuild.cpp +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp @@ -266,8 +266,9 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() { cur_file->offset = file_partition_size; file_partition_size += cur_file->size; cur_file->entry_offset = entry_offset; - entry_offset += sizeof(RomFSFileEntry) + - Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4); + entry_offset += + static_cast<u32>(sizeof(RomFSFileEntry) + + Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4)); prev_file = cur_file; } // Assign deferred parent/sibling ownership. @@ -284,8 +285,9 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() { for (const auto& it : directories) { cur_dir = it.second; cur_dir->entry_offset = entry_offset; - entry_offset += sizeof(RomFSDirectoryEntry) + - Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4); + entry_offset += + static_cast<u32>(sizeof(RomFSDirectoryEntry) + + Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4)); } // Assign deferred parent/sibling ownership. for (auto it = directories.rbegin(); it->second != root; ++it) { diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index a08a70efd..a6101f1c0 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp @@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() { // Read rest of patch while (true) { - if (i + 1 >= lines.size()) + if (i + 1 >= lines.size()) { break; - const auto patch_line = lines[++i]; + } + + const auto& patch_line = lines[++i]; // Start of new patch if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { @@ -297,7 +299,7 @@ void IPSwitchCompiler::Parse() { patch_text->GetName(), offset, Common::HexToString(replace)); } - patch.records.insert_or_assign(offset, std::move(replace)); + patch.records.insert_or_assign(static_cast<u32>(offset), std::move(replace)); } patches.push_back(std::move(patch)); diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 76313679d..ef93ef3ed 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp @@ -2,9 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + #include "common/string_util.h" #include "core/file_sys/kernel_executable.h" #include "core/file_sys/vfs_offset.h" +#include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index 324a57384..044c554d3 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h @@ -4,10 +4,17 @@ #pragma once +#include <array> +#include <vector> + #include "common/common_funcs.h" +#include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/vfs_types.h" -#include "core/loader/loader.h" + +namespace Loader { +enum class ResultStatus : u16; +} namespace FileSys { diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 93d0df6b9..3596541b2 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/vfs.h" namespace FileSys { @@ -107,7 +108,7 @@ std::vector<u8> CNMT::Serialize() const { memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader)); } - auto offset = header.table_offset; + u64_le offset = header.table_offset; for (const auto& rec : content_records) { memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord)); diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 1f82fff0a..53535e5f5 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -10,7 +10,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" namespace FileSys { class CNMT; 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 @@ #include "core/file_sys/nca_patch.h" namespace FileSys { +namespace { +template <bool Subsection, typename BlockType, typename BucketType> +std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockType& block, + const BucketType& buckets) { + if constexpr (Subsection) { + const auto& last_bucket = buckets[block.number_buckets - 1]; + if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) { + return {block.number_buckets - 1, last_bucket.number_entries}; + } + } else { + ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); + } + + std::size_t bucket_id = std::count_if( + block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, + [&offset](u64 base_offset) { return base_offset <= offset; }); + + const auto& bucket = buckets[bucket_id]; + + if (bucket.number_entries == 1) { + return {bucket_id, 0}; + } + + std::size_t low = 0; + std::size_t mid = 0; + std::size_t high = bucket.number_entries - 1; + while (low <= high) { + mid = (low + high) / 2; + if (bucket.entries[mid].address_patch > offset) { + high = mid - 1; + } else { + if (mid == bucket.number_entries - 1 || + bucket.entries[mid + 1].address_patch > offset) { + return {bucket_id, mid}; + } + + low = mid + 1; + } + } + + UNREACHABLE_MSG("Offset could not be found in BKTR block."); +} +} // Anonymous namespace BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, 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 { return raw_read; } -template <bool Subsection, typename BlockType, typename BucketType> -std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, - BucketType buckets) const { - if constexpr (Subsection) { - const auto last_bucket = buckets[block.number_buckets - 1]; - if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) - return {block.number_buckets - 1, last_bucket.number_entries}; - } else { - ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); - } - - std::size_t bucket_id = std::count_if( - block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, - [&offset](u64 base_offset) { return base_offset <= offset; }); - - const auto bucket = buckets[bucket_id]; - - if (bucket.number_entries == 1) - return {bucket_id, 0}; - - std::size_t low = 0; - std::size_t mid = 0; - std::size_t high = bucket.number_entries - 1; - while (low <= high) { - mid = (low + high) / 2; - if (bucket.entries[mid].address_patch > offset) { - high = mid - 1; - } else { - if (mid == bucket.number_entries - 1 || - bucket.entries[mid + 1].address_patch > offset) { - return {bucket_id, mid}; - } - - low = mid + 1; - } - } - - UNREACHABLE_MSG("Offset could not be found in BKTR block."); -} - RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); 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: bool Rename(std::string_view name) override; private: - template <bool Subsection, typename BlockType, typename BucketType> - std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block, - BucketType buckets) const; - RelocationEntry GetRelocationEntry(u64 offset) const; RelocationEntry GetNextRelocationEntry(u64 offset) const; diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 846986736..48a2ed4d4 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -21,7 +21,7 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const { magic == Common::MakeMagic('P', 'F', 'S', '0'); } -PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { +PartitionFilesystem::PartitionFilesystem(VirtualFile file) { // At least be as large as the header if (file->GetSize() < sizeof(Header)) { status = Loader::ResultStatus::ErrorBadPFSHeader; @@ -89,11 +89,11 @@ std::map<std::string, u64> PartitionFilesystem::GetFileSizes() const { return sizes; } -std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { +std::vector<VirtualFile> PartitionFilesystem::GetFiles() const { return pfs_files; } -std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { +std::vector<VirtualDir> PartitionFilesystem::GetSubdirectories() const { return {}; } @@ -101,7 +101,7 @@ std::string PartitionFilesystem::GetName() const { return is_hfs ? "HFS0" : "PFS0"; } -std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { +VirtualDir PartitionFilesystem::GetParentDirectory() const { // TODO(DarkLordZach): Add support for nested containers. return nullptr; } diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 279193b19..0f831148e 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h @@ -24,7 +24,7 @@ namespace FileSys { */ class PartitionFilesystem : public ReadOnlyVfsDirectory { public: - explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); + explicit PartitionFilesystem(VirtualFile file); ~PartitionFilesystem() override; Loader::ResultStatus GetStatus() const; @@ -32,10 +32,10 @@ public: std::map<std::string, u64> GetFileOffsets() const; std::map<std::string, u64> GetFileSizes() const; - std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; - std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; + std::vector<VirtualFile> GetFiles() const override; + std::vector<VirtualDir> GetSubdirectories() const override; std::string GetName() const override; - std::shared_ptr<VfsDirectory> GetParentDirectory() const override; + VirtualDir GetParentDirectory() const override; void PrintDebugInfo() const; private: diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 729dbb5f4..807b05821 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -27,8 +27,9 @@ #include "core/settings.h" namespace FileSys { +namespace { -constexpr u64 SINGLE_BYTE_MODULUS = 0x100; +constexpr u32 SINGLE_BYTE_MODULUS = 0x100; constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{ @@ -36,21 +37,29 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{ "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", }; -std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { +enum class TitleVersionFormat : u8 { + ThreeElements, ///< vX.Y.Z + FourElements, ///< vX.Y.Z.W +}; + +std::string FormatTitleVersion(u32 version, + TitleVersionFormat format = TitleVersionFormat::ThreeElements) { std::array<u8, sizeof(u32)> bytes{}; - bytes[0] = version % SINGLE_BYTE_MODULUS; + bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS); for (std::size_t i = 1; i < bytes.size(); ++i) { version /= SINGLE_BYTE_MODULUS; - bytes[i] = version % SINGLE_BYTE_MODULUS; + bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS); } - if (format == TitleVersionFormat::FourElements) + if (format == TitleVersionFormat::FourElements) { return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); + } return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); } -std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir, - std::string_view name) { +// Returns a directory with name matching name case-insensitive. Returns nullptr if directory +// doesn't have a directory with name. +VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { #ifdef _WIN32 return dir->GetSubdirectory(name); #else @@ -66,6 +75,43 @@ std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<Vfs #endif } +std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( + u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) { + const auto build_id_raw = Common::HexToString(build_id_, upper); + const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); + const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); + + if (file == nullptr) { + LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", + title_id, build_id); + return std::nullopt; + } + + std::vector<u8> data(file->GetSize()); + if (file->Read(data.data(), data.size()) != data.size()) { + LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", + title_id, build_id); + return std::nullopt; + } + + const Core::Memory::TextCheatParser parser; + return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); +} + +void AppendCommaIfNotEmpty(std::string& to, std::string_view with) { + if (to.empty()) { + to += with; + } else { + to += ", "; + to += with; + } +} + +bool IsDirValidAndNonEmpty(const VirtualDir& dir) { + return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); +} +} // Anonymous namespace + PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} PatchManager::~PatchManager() = default; @@ -246,7 +292,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st return out; } -bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { +bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { const auto build_id_raw = Common::HexToString(build_id_); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); @@ -266,36 +312,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { return !CollectPatches(patch_dirs, build_id).empty(); } -namespace { -std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( - const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_, - const VirtualDir& base_path, bool upper) { - const auto build_id_raw = Common::HexToString(build_id_, upper); - const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); - const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); - - if (file == nullptr) { - LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", - title_id, build_id); - return std::nullopt; - } - - std::vector<u8> data(file->GetSize()); - if (file->Read(data.data(), data.size()) != data.size()) { - LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", - title_id, build_id); - return std::nullopt; - } - - Core::Memory::TextCheatParser parser; - return parser.Parse(system, - std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); -} - -} // Anonymous namespace - std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( - const Core::System& system, const std::array<u8, 32>& build_id_) const { + const Core::System& system, const BuildID& build_id_) const { const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); @@ -315,14 +333,12 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); if (cheats_dir != nullptr) { - auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); - if (res.has_value()) { + if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) { std::copy(res->begin(), res->end(), std::back_inserter(out)); continue; } - res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); - if (res.has_value()) { + if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) { std::copy(res->begin(), res->end(), std::back_inserter(out)); } } @@ -436,21 +452,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content return romfs; } -static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { - if (to.empty()) - to += with; - else - to += ", " + with; -} - -static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { - return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); -} - -std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( - VirtualFile update_raw) const { - if (title_id == 0) +PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { + if (title_id == 0) { return {}; + } + std::map<std::string, std::string, std::less<>> out; const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto& disabled = Settings::values.disabled_addons[title_id]; @@ -473,8 +479,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam if (meta_ver.value_or(0) == 0) { out.insert_or_assign(update_label, ""); } else { - out.insert_or_assign( - update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); + out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver)); } } else if (update_raw != nullptr) { out.insert_or_assign(update_label, "PACKED"); @@ -563,40 +568,46 @@ std::optional<u32> PatchManager::GetGameVersion() const { return installed.GetEntryVersion(title_id); } -std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { +PatchManager::Metadata PatchManager::GetControlMetadata() const { const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); - if (base_control_nca == nullptr) + if (base_control_nca == nullptr) { return {}; + } return ParseControlNCA(*base_control_nca); } -std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { +PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const { const auto base_romfs = nca.GetRomFS(); - if (base_romfs == nullptr) + if (base_romfs == nullptr) { return {}; + } const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); - if (romfs == nullptr) + if (romfs == nullptr) { return {}; + } const auto extracted = ExtractRomFS(romfs); - if (extracted == nullptr) + if (extracted == nullptr) { return {}; + } auto nacp_file = extracted->GetFile("control.nacp"); - if (nacp_file == nullptr) + if (nacp_file == nullptr) { nacp_file = extracted->GetFile("Control.nacp"); + } auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); VirtualFile icon_file; for (const auto& language : FileSys::LANGUAGE_NAMES) { - icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); - if (icon_file != nullptr) + icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat")); + if (icon_file != nullptr) { break; + } } 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 f4cb918dd..1f28c6241 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -6,10 +6,11 @@ #include <map> #include <memory> +#include <optional> #include <string> #include "common/common_types.h" #include "core/file_sys/nca_metadata.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" #include "core/memory/dmnt_cheat_types.h" namespace Core { @@ -21,71 +22,62 @@ namespace FileSys { class NCA; class NACP; -enum class TitleVersionFormat : u8 { - ThreeElements, ///< vX.Y.Z - FourElements, ///< vX.Y.Z.W -}; - -std::string FormatTitleVersion(u32 version, - TitleVersionFormat format = TitleVersionFormat::ThreeElements); - -// Returns a directory with name matching name case-insensitive. Returns nullptr if directory -// doesn't have a directory with name. -std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir, - std::string_view name); - // A centralized class to manage patches to games. class PatchManager { public: + using BuildID = std::array<u8, 0x20>; + using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; + using PatchVersionNames = std::map<std::string, std::string, std::less<>>; + explicit PatchManager(u64 title_id); ~PatchManager(); - u64 GetTitleID() const; + [[nodiscard]] u64 GetTitleID() const; // Currently tracked ExeFS patches: // - Game Updates - VirtualDir PatchExeFS(VirtualDir exefs) const; + [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const; // Currently tracked NSO patches: // - IPS // - IPSwitch - std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; + [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso, + const std::string& name) const; // Checks to see if PatchNSO() will have any effect given the NSO's build ID. // Used to prevent expensive copies in NSO loader. - bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; + [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const; // Creates a CheatList object with all - std::vector<Core::Memory::CheatEntry> CreateCheatList( - const Core::System& system, const std::array<u8, 0x20>& build_id) const; + [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( + const Core::System& system, const BuildID& build_id) const; // Currently tracked RomFS patches: // - Game Updates // - LayeredFS - VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, - ContentRecordType type = ContentRecordType::Program, - VirtualFile update_raw = nullptr) const; + [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, + ContentRecordType type = ContentRecordType::Program, + VirtualFile update_raw = nullptr) const; // Returns a vector of pairs between patch names and patch versions. // i.e. Update 3.2.2 will return {"Update", "3.2.2"} - std::map<std::string, std::string, std::less<>> GetPatchVersionNames( - VirtualFile update_raw = nullptr) const; + [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const; // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be // std::nullopt - std::optional<u32> GetGameVersion() const; + [[nodiscard]] std::optional<u32> GetGameVersion() const; // Given title_id of the program, attempts to get the control data of the update and parse // it, falling back to the base control data. - std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; + [[nodiscard]] Metadata GetControlMetadata() const; // Version of GetControlMetadata that takes an arbitrary NCA - std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; + [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const; private: - std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, - const std::string& build_id) const; + [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, + const std::string& build_id) const; u64 title_id; }; diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 43169bf9f..9cf49bf44 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/file_sys/program_metadata.h" +#include "core/file_sys/vfs.h" #include "core/loader/loader.h" namespace FileSys { diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 35069972b..455532567 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -9,7 +9,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index e42b677f7..da01002d5 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const { for (const auto& sdir : dir->GetSubdirectories()) { for (const auto& file : sdir->GetFiles()) { const auto name = file->GetName(); - if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && - name[35] == 'a') { + if (name.length() == 36 && name.ends_with(".nca")) { out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); } } @@ -621,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists, const VfsCopyFunction& copy) { - CNMTHeader header{ - nca.GetTitleId(), // Title ID - 0, // Ignore/Default title version - type, // Type - {}, // Padding - 0x10, // Default table offset - 1, // 1 Content Entry - 0, // No Meta Entries - {}, // Padding - {}, // Reserved 1 - 0, // Is committed - 0, // Required download system version - {}, // Reserved 2 + const CNMTHeader header{ + .title_id = nca.GetTitleId(), + .title_version = 0, + .type = type, + .reserved = {}, + .table_offset = 0x10, + .number_content_entries = 1, + .number_meta_entries = 0, + .attributes = 0, + .reserved2 = {}, + .is_committed = 0, + .required_download_system_version = 0, + .reserved3 = {}, }; - OptionalHeader opt_header{0, 0}; + const OptionalHeader opt_header{0, 0}; ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); - memcpy(&c_rec.nca_id, &c_rec.hash, 16); + std::memcpy(&c_rec.nca_id, &c_rec.hash, 16); const CNMT new_cnmt(header, opt_header, {c_rec}, {}); if (!RawInstallYuzuMeta(new_cnmt)) { return InstallResult::ErrorMetaFailed; diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 2fd07ed04..82e683782 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -4,7 +4,6 @@ #pragma once -#include <array> #include "core/file_sys/vfs.h" namespace FileSys { 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 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "core/core.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" @@ -19,7 +18,9 @@ namespace FileSys { -RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { +RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider, + Service::FileSystem::FileSystemController& controller) + : content_provider{provider}, filesystem_controller{controller} { // Load the RomFS from the app if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { LOG_ERROR(Service_FS, "Unable to read RomFS!"); @@ -46,39 +47,38 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_titl ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) const { - std::shared_ptr<NCA> res; - - switch (storage) { - case StorageId::None: - res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); - break; - case StorageId::NandSystem: - res = - Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry( - title_id, type); - break; - case StorageId::NandUser: - res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry( - title_id, type); - break; - case StorageId::SdCard: - res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry( - title_id, type); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); - } - + const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type); if (res == nullptr) { // TODO(DarkLordZach): Find the right error code to use here return RESULT_UNKNOWN; } + const auto romfs = res->GetRomFS(); if (romfs == nullptr) { // TODO(DarkLordZach): Find the right error code to use here return RESULT_UNKNOWN; } + return MakeResult<VirtualFile>(romfs); } +std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage, + ContentRecordType type) const { + switch (storage) { + case StorageId::None: + return content_provider.GetEntry(title_id, type); + case StorageId::NandSystem: + return filesystem_controller.GetSystemNANDContents()->GetEntry(title_id, type); + case StorageId::NandUser: + return filesystem_controller.GetUserNANDContents()->GetEntry(title_id, type); + case StorageId::SdCard: + return filesystem_controller.GetSDMCContents()->GetEntry(title_id, type); + case StorageId::Host: + case StorageId::GameCard: + default: + UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); + return nullptr; + } +} + } // 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 { class AppLoader; } // namespace Loader +namespace Service::FileSystem { +class FileSystemController; +} + namespace FileSys { +class ContentProvider; +class NCA; + enum class ContentRecordType : u8; enum class StorageId : u8 { @@ -29,18 +36,26 @@ enum class StorageId : u8 { /// File system interface to the RomFS archive class RomFSFactory { public: - explicit RomFSFactory(Loader::AppLoader& app_loader); + explicit RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider, + Service::FileSystem::FileSystemController& controller); ~RomFSFactory(); void SetPackedUpdate(VirtualFile update_raw); - ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; - ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; + [[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; + [[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, + ContentRecordType type) const; private: + [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage, + ContentRecordType type) const; + VirtualFile file; VirtualFile update_raw; bool updatable; u64 ivfc_offset; + + ContentProvider& content_provider; + Service::FileSystem::FileSystemController& filesystem_controller; }; } // namespace FileSys diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index 6f732e4d8..cb56d8f2d 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp @@ -5,8 +5,8 @@ #include <memory> #include "core/file_sys/registered_cache.h" #include "core/file_sys/sdmc_factory.h" +#include "core/file_sys/vfs.h" #include "core/file_sys/xts_archive.h" -#include "core/settings.h" namespace FileSys { diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 42dc4e08a..2bb92ba93 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h @@ -5,7 +5,7 @@ #pragma once #include <memory> -#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_types.h" #include "core/hle/result.h" namespace FileSys { diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 175a8266a..90641d23b 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -19,42 +19,10 @@ #include "core/loader/loader.h" namespace FileSys { -namespace { -void SetTicketKeys(const std::vector<VirtualFile>& files) { - auto& keys = Core::Crypto::KeyManager::Instance(); - - for (const auto& ticket_file : files) { - if (ticket_file == nullptr) { - continue; - } - - if (ticket_file->GetExtension() != "tik") { - continue; - } - - if (ticket_file->GetSize() < - Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { - continue; - } - - Core::Crypto::Key128 key{}; - ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); - - // We get the name without the extension in order to create the rights ID. - std::string name_only(ticket_file->GetName()); - name_only.erase(name_only.size() - 4); - - const auto rights_id_raw = Common::HexStringToArray<16>(name_only); - u128 rights_id; - std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); - keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); - } -} -} // Anonymous namespace NSP::NSP(VirtualFile file_) : file(std::move(file_)), status{Loader::ResultStatus::Success}, - pfs(std::make_shared<PartitionFilesystem>(file)) { + pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { if (pfs->GetStatus() != Loader::ResultStatus::Success) { status = pfs->GetStatus(); return; @@ -232,6 +200,35 @@ VirtualDir NSP::GetParentDirectory() const { return file->GetContainingDirectory(); } +void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) { + for (const auto& ticket_file : files) { + if (ticket_file == nullptr) { + continue; + } + + if (ticket_file->GetExtension() != "tik") { + continue; + } + + if (ticket_file->GetSize() < + Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { + continue; + } + + Core::Crypto::Key128 key{}; + ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); + + // We get the name without the extension in order to create the rights ID. + std::string name_only(ticket_file->GetName()); + name_only.erase(name_only.size() - 4); + + const auto rights_id_raw = Common::HexStringToArray<16>(name_only); + u128 rights_id; + std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); + keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + } +} + void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { exefs = pfs; @@ -267,9 +264,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { } const CNMT cnmt(inner_file); - auto& ncas_title = ncas[cnmt.GetTitleID()]; - ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; + ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca; + for (const auto& rec : cnmt.GetContentRecords()) { const auto id_string = Common::HexToString(rec.nca_id, false); auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); @@ -286,13 +283,32 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { } auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); + if (next_nca->GetType() == NCAContentType::Program) { - program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); + program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); } - if (next_nca->GetStatus() == Loader::ResultStatus::Success || - (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && - (cnmt.GetTitleID() & 0x800) != 0)) { - ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); + + if (next_nca->GetStatus() != Loader::ResultStatus::Success && + next_nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + continue; + } + + // If the last 3 hexadecimal digits of the CNMT TitleID is 0x800 or is missing the + // BKTRBaseRomFS, this is an update NCA. Otherwise, this is a base NCA. + if ((cnmt.GetTitleID() & 0x800) != 0 || + next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { + // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and + // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular + // update NCA. + if ((next_nca->GetTitleId() & 0x7FF) != 0 && + (next_nca->GetTitleId() & 0x800) == 0) { + ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = + std::move(next_nca); + } else { + ncas[cnmt.GetTitleID()][{cnmt.GetType(), rec.type}] = std::move(next_nca); + } + } else { + ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca); } } diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index cf89de6a9..c70a11b5b 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -10,6 +10,10 @@ #include "common/common_types.h" #include "core/file_sys/vfs.h" +namespace Core::Crypto { +class KeyManager; +} + namespace Loader { enum class ResultStatus : u16; } @@ -59,6 +63,7 @@ public: VirtualDir GetParentDirectory() const override; private: + void SetTicketKeys(const std::vector<VirtualFile>& files); void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); void ReadNCAs(const std::vector<VirtualFile>& files); @@ -73,7 +78,7 @@ private: std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; std::vector<VirtualFile> ticket_files; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); + Core::Crypto::KeyManager& keys; VirtualFile romfs; VirtualDir exefs; diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index a4c3f67c4..b2f026b6d 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -169,11 +169,12 @@ VfsDirectory::~VfsDirectory() = default; std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { u8 out{}; - std::size_t size = Read(&out, 1, offset); - if (size == 1) + const std::size_t size = Read(&out, sizeof(u8), offset); + if (size == 1) { return out; + } - return {}; + return std::nullopt; } std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index c96f88488..7714d3de5 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t } std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { - if (r_offset < size) - return file->ReadByte(offset + r_offset); + if (r_offset >= size) { + return std::nullopt; + } - return {}; + return file->ReadByte(offset + r_offset); } std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 1dbf632c1..488687ba9 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -72,8 +72,10 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (cache.find(path) != cache.end()) { - auto weak = cache[path]; + + if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) { + const auto& weak = weak_iter->second; + if (!weak.expired()) { return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); } @@ -84,7 +86,7 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { } auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str()); - cache[path] = backing; + cache.insert_or_assign(path, backing); // Cannot use make_shared as RealVfsFile constructor is private return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); @@ -116,11 +118,12 @@ VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); + const auto cached_file_iter = cache.find(old_path); - if (cache.find(old_path) != cache.end()) { - auto file = cache[old_path].lock(); + if (cached_file_iter != cache.cend()) { + auto file = cached_file_iter->second.lock(); - if (!cache[old_path].expired()) { + if (!cached_file_iter->second.expired()) { file->Close(); } @@ -131,7 +134,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ cache.erase(old_path); file->Open(new_path, "r+b"); - cache[new_path] = file; + cache.insert_or_assign(new_path, std::move(file)); } else { UNREACHABLE(); return nullptr; @@ -142,12 +145,15 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - if (cache.find(path) != cache.end()) { - if (!cache[path].expired()) { - cache[path].lock()->Close(); + const auto cached_iter = cache.find(path); + + if (cached_iter != cache.cend()) { + if (!cached_iter->second.expired()) { + cached_iter->second.lock()->Close(); } cache.erase(path); } + return FS::Delete(path); } @@ -192,21 +198,25 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, } for (auto& kv : cache) { - // Path in cache starts with old_path - if (kv.first.rfind(old_path, 0) == 0) { - const auto file_old_path = - FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); - const auto file_new_path = - FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), - FS::DirectorySeparator::PlatformDefault); - auto cached = cache[file_old_path]; - if (!cached.expired()) { - auto file = cached.lock(); - file->Open(file_new_path, "r+b"); - cache.erase(file_old_path); - cache[file_new_path] = file; - } + // If the path in the cache doesn't start with old_path, then bail on this file. + if (kv.first.rfind(old_path, 0) != 0) { + continue; + } + + const auto file_old_path = + FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); + auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), + FS::DirectorySeparator::PlatformDefault); + const auto& cached = cache[file_old_path]; + + if (cached.expired()) { + continue; } + + auto file = cached.lock(); + file->Open(file_new_path, "r+b"); + cache.erase(file_old_path); + cache.insert_or_assign(std::move(file_new_path), std::move(file)); } return OpenDirectory(new_path, Mode::ReadWrite); @@ -214,15 +224,21 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + for (auto& kv : cache) { - // Path in cache starts with old_path - if (kv.first.rfind(path, 0) == 0) { - if (!cache[kv.first].expired()) { - cache[kv.first].lock()->Close(); - } - cache.erase(kv.first); + // If the path in the cache doesn't start with path, then bail on this file. + if (kv.first.rfind(path, 0) != 0) { + continue; } + + const auto& entry = cache[kv.first]; + if (!entry.expired()) { + entry.lock()->Close(); + } + + cache.erase(kv.first); } + return FS::DeleteDirRecursively(path); } @@ -260,14 +276,14 @@ bool RealVfsFile::IsReadable() const { } std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { - if (!backing->Seek(offset, SEEK_SET)) { + if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { return 0; } return backing->ReadBytes(data, length); } std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { - if (!backing->Seek(offset, SEEK_SET)) { + if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { return 0; } return backing->WriteBytes(data, length); diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h index 9f5a90b1b..8b27c30fa 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs_static.h @@ -54,9 +54,11 @@ public: } std::optional<u8> ReadByte(std::size_t offset) const override { - if (offset < size) - return value; - return {}; + if (offset >= size) { + return std::nullopt; + } + + return value; } std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index ccf5966d0..24c58e7ae 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -15,8 +15,9 @@ #include "common/hex_util.h" #include "common/string_util.h" #include "core/crypto/aes_util.h" +#include "core/crypto/key_manager.h" #include "core/crypto/xts_encryption_layer.h" -#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/content_archive.h" #include "core/file_sys/vfs_offset.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" @@ -43,7 +44,9 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t return true; } -NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { +NAX::NAX(VirtualFile file_) + : header(std::make_unique<NAXHeader>()), + file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { std::string path = Common::FS::SanitizePath(file->GetFullPath()); static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", std::regex_constants::ECMAScript | @@ -60,7 +63,8 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m } NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) - : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { + : header(std::make_unique<NAXHeader>()), + file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { Core::Crypto::SHA256Hash hash{}; mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 563531bb6..c472e226e 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h @@ -9,12 +9,16 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/crypto/key_manager.h" -#include "core/file_sys/content_archive.h" #include "core/file_sys/vfs.h" -#include "core/loader/loader.h" + +namespace Loader { +enum class ResultStatus : u16; +} namespace FileSys { +class NCA; + struct NAXHeader { std::array<u8, 0x20> hmac; u64_le magic; @@ -62,6 +66,6 @@ private: VirtualFile dec_file; - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); + Core::Crypto::KeyManager& keys; }; } // namespace FileSys diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp new file mode 100644 index 000000000..c5d65f2d0 --- /dev/null +++ b/src/core/frontend/applets/controller.cpp @@ -0,0 +1,81 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/frontend/applets/controller.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/hid.h" +#include "core/hle/service/sm/sm.h" + +namespace Core::Frontend { + +ControllerApplet::~ControllerApplet() = default; + +DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) + : service_manager{service_manager_} {} + +DefaultControllerApplet::~DefaultControllerApplet() = default; + +void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, + ControllerParameters parameters) const { + LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); + + auto& npad = + service_manager.GetService<Service::HID::Hid>("hid") + ->GetAppletResource() + ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); + + auto& players = Settings::values.players; + + const std::size_t min_supported_players = + parameters.enable_single_mode ? 1 : parameters.min_players; + + // Disconnect Handheld first. + npad.DisconnectNPadAtIndex(8); + + // Deduce the best configuration based on the input parameters. + for (std::size_t index = 0; index < players.size() - 2; ++index) { + // First, disconnect all controllers regardless of the value of keep_controllers_connected. + // This makes it easy to connect the desired controllers. + npad.DisconnectNPadAtIndex(index); + + // Only connect the minimum number of required players. + if (index >= min_supported_players) { + continue; + } + + // Connect controllers based on the following priority list from highest to lowest priority: + // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld + if (parameters.allow_pro_controller) { + npad.AddNewControllerAt( + npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); + } else if (parameters.allow_dual_joycons) { + npad.AddNewControllerAt( + npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); + } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { + // Assign left joycons to even player indices and right joycons to odd player indices. + // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and + // a right Joycon for Player 2 in 2 Player Assist mode. + if (index % 2 == 0) { + npad.AddNewControllerAt( + npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); + } else { + npad.AddNewControllerAt( + npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); + } + } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && + !Settings::values.use_docked_mode) { + // We should *never* reach here under any normal circumstances. + npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), + index); + } else { + UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); + } + } + + callback(); +} + +} // namespace Core::Frontend diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h new file mode 100644 index 000000000..3e49cdbb9 --- /dev/null +++ b/src/core/frontend/applets/controller.h @@ -0,0 +1,56 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> + +#include "common/common_types.h" + +namespace Service::SM { +class ServiceManager; +} + +namespace Core::Frontend { + +using BorderColor = std::array<u8, 4>; +using ExplainText = std::array<char, 0x81>; + +struct ControllerParameters { + s8 min_players{}; + s8 max_players{}; + bool keep_controllers_connected{}; + bool enable_single_mode{}; + bool enable_border_color{}; + std::vector<BorderColor> border_colors{}; + bool enable_explain_text{}; + std::vector<ExplainText> explain_text{}; + bool allow_pro_controller{}; + bool allow_handheld{}; + bool allow_dual_joycons{}; + bool allow_left_joycon{}; + bool allow_right_joycon{}; +}; + +class ControllerApplet { +public: + virtual ~ControllerApplet(); + + virtual void ReconfigureControllers(std::function<void()> callback, + ControllerParameters parameters) const = 0; +}; + +class DefaultControllerApplet final : public ControllerApplet { +public: + explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); + ~DefaultControllerApplet() override; + + void ReconfigureControllers(std::function<void()> callback, + ControllerParameters parameters) const override; + +private: + Service::SM::ServiceManager& service_manager; +}; + +} // namespace Core::Frontend diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 9a081fbd4..8c1193894 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -84,10 +84,12 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { return; std::lock_guard guard{touch_state->mutex}; - touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / - (framebuffer_layout.screen.right - framebuffer_layout.screen.left); - touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / - (framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); + touch_state->touch_x = + static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) / + static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left); + touch_state->touch_y = + static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) / + static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top); touch_state->touch_pressed = true; } diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index c1fbc235b..1acc82497 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -14,8 +14,8 @@ namespace Layout { template <class T> static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area, float screen_aspect_ratio) { - float scale = std::min(static_cast<float>(window_area.GetWidth()), - window_area.GetHeight() / screen_aspect_ratio); + const float scale = std::min(static_cast<float>(window_area.GetWidth()), + static_cast<float>(window_area.GetHeight()) / screen_aspect_ratio); return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), static_cast<T>(std::round(scale * screen_aspect_ratio))}; } @@ -27,7 +27,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { // so just calculate them both even if the other isn't showing. FramebufferLayout res{width, height, false, {}}; - const float window_aspect_ratio = static_cast<float>(height) / width; + const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width); const float emulation_aspect_ratio = EmulationAspectRatio( static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio); diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 91ecc30ab..e2e3bbbb3 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -4,6 +4,7 @@ #pragma once +#include "common/common_types.h" #include "common/math_util.h" namespace Layout { diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 2b098b7c6..277b70e53 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -33,6 +33,9 @@ public: virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { return {}; } + virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { + return {}; + } }; /// An abstract class template for a factory that can create input devices. @@ -119,11 +122,11 @@ using ButtonDevice = InputDevice<bool>; using AnalogDevice = InputDevice<std::tuple<float, float>>; /** - * A motion device is an input device that returns a tuple of accelerometer state vector and - * gyroscope state vector. + * A motion status is an object that returns a tuple of accelerometer state vector, + * gyroscope state vector, rotation state vector and orientation state matrix. * * For both vectors: - * x+ is the same direction as LEFT on D-pad. + * x+ is the same direction as RIGHT on D-pad. * y+ is normal to the touch screen, pointing outward. * z+ is the same direction as UP on D-pad. * @@ -133,8 +136,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>; * For gyroscope state vector: * Orientation is determined by right-hand rule. * Units: deg/sec + * + * For rotation state vector + * Units: rotations + * + * For orientation state matrix + * x vector + * y vector + * z vector + */ +using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>, + std::array<Common::Vec3f, 3>>; + +/** + * A motion device is an input device that returns a motion status object */ -using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; +using MotionDevice = InputDevice<MotionStatus>; /** * 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/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 79f22a403..97ee65464 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -291,11 +291,11 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr) */ static u8 HexCharToValue(u8 hex) { if (hex >= '0' && hex <= '9') { - return hex - '0'; + return static_cast<u8>(hex - '0'); } else if (hex >= 'a' && hex <= 'f') { - return hex - 'a' + 0xA; + return static_cast<u8>(hex - 'a' + 0xA); } else if (hex >= 'A' && hex <= 'F') { - return hex - 'A' + 0xA; + return static_cast<u8>(hex - 'A' + 0xA); } LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex); @@ -310,9 +310,9 @@ static u8 HexCharToValue(u8 hex) { static u8 NibbleToHex(u8 n) { n &= 0xF; if (n < 0xA) { - return '0' + n; + return static_cast<u8>('0' + n); } else { - return 'a' + n - 0xA; + return static_cast<u8>('a' + n - 0xA); } } @@ -355,8 +355,8 @@ static u64 HexToLong(const u8* src, std::size_t len) { */ static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) { while (len-- > 0) { - u8 tmp = *src++; - *dest++ = NibbleToHex(tmp >> 4); + const u8 tmp = *src++; + *dest++ = NibbleToHex(static_cast<u8>(tmp >> 4)); *dest++ = NibbleToHex(tmp); } } @@ -370,7 +370,7 @@ static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) { */ static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) { while (len-- > 0) { - *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); + *dest++ = static_cast<u8>((HexCharToValue(src[0]) << 4) | HexCharToValue(src[1])); src += 2; } } @@ -602,22 +602,22 @@ static void SendReply(const char* reply) { memcpy(command_buffer + 1, reply, command_length); - u8 checksum = CalculateChecksum(command_buffer, command_length + 1); + const u8 checksum = CalculateChecksum(command_buffer, command_length + 1); command_buffer[0] = GDB_STUB_START; command_buffer[command_length + 1] = GDB_STUB_END; - command_buffer[command_length + 2] = NibbleToHex(checksum >> 4); + command_buffer[command_length + 2] = NibbleToHex(static_cast<u8>(checksum >> 4)); command_buffer[command_length + 3] = NibbleToHex(checksum); u8* ptr = command_buffer; u32 left = command_length + 4; while (left > 0) { - int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); + const auto sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0); if (sent_size < 0) { LOG_ERROR(Debug_GDBStub, "gdb: send failed"); return Shutdown(); } - left -= sent_size; + left -= static_cast<u32>(sent_size); ptr += sent_size; } } @@ -777,10 +777,10 @@ static void ReadCommand() { command_buffer[command_length++] = c; } - u8 checksum_received = HexCharToValue(ReadByte()) << 4; - checksum_received |= HexCharToValue(ReadByte()); + auto checksum_received = static_cast<u32>(HexCharToValue(ReadByte()) << 4); + checksum_received |= static_cast<u32>(HexCharToValue(ReadByte())); - u8 checksum_calculated = CalculateChecksum(command_buffer, command_length); + const u32 checksum_calculated = CalculateChecksum(command_buffer, command_length); if (checksum_received != checksum_calculated) { LOG_ERROR(Debug_GDBStub, diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 1b503331f..1c354037d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -38,10 +38,11 @@ public: explicit RequestHelperBase(Kernel::HLERequestContext& context) : context(&context), cmdbuf(context.CommandBuffer()) {} - void Skip(unsigned size_in_words, bool set_to_null) { - if (set_to_null) + void Skip(u32 size_in_words, bool set_to_null) { + if (set_to_null) { memset(cmdbuf + index, 0, size_in_words * sizeof(u32)); - index += size_in_words; + } + index += static_cast<ptrdiff_t>(size_in_words); } /** @@ -49,15 +50,15 @@ public: */ void AlignWithPadding() { if (index & 3) { - Skip(4 - (index & 3), true); + Skip(static_cast<u32>(4 - (index & 3)), true); } } - unsigned GetCurrentOffset() const { - return static_cast<unsigned>(index); + u32 GetCurrentOffset() const { + return static_cast<u32>(index); } - void SetCurrentOffset(unsigned offset) { + void SetCurrentOffset(u32 offset) { index = static_cast<ptrdiff_t>(offset); } }; @@ -89,7 +90,7 @@ public: // The entire size of the raw data section in u32 units, including the 16 bytes of mandatory // padding. - u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size; + u64 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size; u32 num_handles_to_move{}; u32 num_domain_objects{}; @@ -105,7 +106,7 @@ public: raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects; } - header.data_size.Assign(raw_data_size); + header.data_size.Assign(static_cast<u32>(raw_data_size)); if (num_handles_to_copy || num_handles_to_move) { header.enable_handle_descriptor.Assign(1); } 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 } ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, - Core::Memory::Memory& memory) { + Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { // Keep ServerSession alive until we're done working with it. if (!parent->Server()) { return ERR_SESSION_CLOSED_BY_REMOTE; } // Signal the server session that new data is available - return parent->Server()->HandleSyncRequest(std::move(thread), memory); + return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing); } } // 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 { class Memory; } +namespace Core::Timing { +class CoreTiming; +} + namespace Kernel { class KernelCore; @@ -42,7 +46,8 @@ public: return HANDLE_TYPE; } - ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); + ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); bool ShouldWait(const Thread* thread) const override; diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index fb30b6f8b..3e745c18b 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -118,7 +118,7 @@ std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const { void HandleTable::Clear() { for (u16 i = 0; i < table_size; ++i) { - generations[i] = i + 1; + generations[i] = static_cast<u16>(i + 1); objects[i] = nullptr; } next_free_slot = 0; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index cabe8d418..f2b0fe2fd 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -219,6 +219,7 @@ struct KernelCore::Impl { return static_cast<u32>(system.GetCpuManager().CurrentCore()); } } + std::unique_lock lock{register_thread_mutex}; const auto it = host_thread_ids.find(this_id); if (it == host_thread_ids.end()) { return Core::INVALID_HOST_THREAD_ID; @@ -324,7 +325,7 @@ struct KernelCore::Impl { std::unordered_map<std::thread::id, u32> host_thread_ids; u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; - std::mutex register_thread_mutex; + mutable std::mutex register_thread_mutex; // Kernel memory management std::unique_ptr<Memory::MemoryManager> memory_manager; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index a4b234424..6b7db5372 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -72,7 +72,7 @@ u32 GlobalScheduler::SelectThreads() { if (top_thread != nullptr) { // TODO(Blinkhawk): Implement Thread Pinning } else { - idle_cores |= (1ul << core); + idle_cores |= (1U << core); } top_threads[core] = top_thread; } @@ -126,7 +126,7 @@ u32 GlobalScheduler::SelectThreads() { top_threads[core_id] = suggested; } - idle_cores &= ~(1ul << core_id); + idle_cores &= ~(1U << core_id); } u32 cores_needing_context_switch{}; for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { @@ -134,7 +134,7 @@ u32 GlobalScheduler::SelectThreads() { ASSERT(top_threads[core] == nullptr || static_cast<u32>(top_threads[core]->GetProcessorID()) == core); if (update_thread(top_threads[core], sched)) { - cores_needing_context_switch |= (1ul << core); + cores_needing_context_switch |= (1U << core); } } return cores_needing_context_switch; @@ -364,7 +364,7 @@ void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule, } else { must_context_switch = true; } - cores_pending_reschedule &= ~(1ul << core); + cores_pending_reschedule &= ~(1U << core); } if (must_context_switch) { auto& core_scheduler = kernel.CurrentScheduler(); @@ -756,14 +756,18 @@ void Scheduler::SwitchToCurrent() { current_thread = selected_thread; is_context_switch_pending = false; } - while (!is_context_switch_pending) { + const auto is_switch_pending = [this] { + std::scoped_lock lock{guard}; + return is_context_switch_pending; + }; + do { if (current_thread != nullptr && !current_thread->IsHLEThread()) { current_thread->context_guard.lock(); if (!current_thread->IsRunnable()) { current_thread->context_guard.unlock(); break; } - if (current_thread->GetProcessorID() != core_id) { + if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) { current_thread->context_guard.unlock(); break; } @@ -775,7 +779,7 @@ void Scheduler::SwitchToCurrent() { next_context = &idle_thread->GetHostContext(); } Common::Fiber::YieldTo(switch_fiber, *next_context); - } + } while (!is_switch_pending()); } } diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 36e3c26fb..b6f04dcea 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -188,7 +188,7 @@ private: /// Scheduler lock mechanisms. bool is_locked{}; - Common::SpinLock inner_lock{}; + std::mutex inner_lock; std::atomic<s64> scope_lock{}; Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; 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 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -185,10 +184,11 @@ ResultCode ServerSession::CompleteSyncRequest() { } ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, - Core::Memory::Memory& memory) { + Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing) { const ResultCode result = QueueSyncRequest(std::move(thread), memory); const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; - Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); + core_timing.ScheduleEvent(delay, request_event, {}); return result; } 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; } namespace Core::Timing { +class CoreTiming; struct EventType; -} +} // namespace Core::Timing namespace Kernel { @@ -87,12 +88,14 @@ public: /** * Handle a sync request from the emulated application. * - * @param thread Thread that initiated the request. - * @param memory Memory context to handle the sync request under. + * @param thread Thread that initiated the request. + * @param memory Memory context to handle the sync request under. + * @param core_timing Core timing context to schedule the request event under. * * @returns ResultCode from the operation. */ - ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); + ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, + Core::Timing::CoreTiming& core_timing); bool ShouldWait(const Thread* thread) const override; 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) { SchedulerLock lock(system.Kernel()); thread->InvalidateHLECallback(); thread->SetStatus(ThreadStatus::WaitIPC); - session->SendSyncRequest(SharedFrom(thread), system.Memory()); + session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } if (thread->HasHLECallback()) { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index eb54cb123..2850dd805 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -496,7 +496,7 @@ public: {3, nullptr, "LoadIdTokenCache"}, {130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"}, {150, nullptr, "CreateAuthorizationRequest"}, - {160, nullptr, "StoreOpenContext"}, + {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"}, {170, nullptr, "LoadNetworkServiceLicenseKindAsync"}, }; // clang-format on @@ -520,6 +520,12 @@ private: rb.PushRaw<u64>(user_id.GetNintendoID()); } + void StoreOpenContext(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + Common::UUID user_id; }; @@ -774,6 +780,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + + // This is similar to GetBaasAccountManagerForApplication + // This command is used concurrently with ListOpenContextStoredUsers + // TODO: Find the differences between this and GetBaasAccountManagerForApplication + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser()); +} + void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index d4c6395c6..c611efd89 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -34,6 +34,7 @@ public: void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); void GetProfileEditor(Kernel::HLERequestContext& ctx); void ListQualifiedUsers(Kernel::HLERequestContext& ctx); + void LoadOpenContext(Kernel::HLERequestContext& ctx); void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); private: diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index cb44e06b7..75a24f8f5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {110, nullptr, "StoreSaveDataThumbnail"}, {111, nullptr, "ClearSaveDataThumbnail"}, {120, nullptr, "CreateGuestLoginRequest"}, - {130, nullptr, "LoadOpenContext"}, // 5.0.0+ + {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ 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_) {120, nullptr, "ExecuteProgram"}, {121, nullptr, "ClearUserChannel"}, {122, nullptr, "UnpopToUserChannel"}, - {123, nullptr, "GetPreviousProgramIndex"}, + {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, @@ -1554,6 +1554,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque rb.Push<u32>(0); } +void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(previous_program_index); +} + void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); 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: void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); + void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); bool launch_popped_application_specific = false; bool launch_popped_account_preselect = false; + s32 previous_program_index{-1}; Kernel::EventPair gpu_error_detected_event; Kernel::EventPair friend_invitation_storage_channel_event; Core::System& system; diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index c3261f3e6..2b626bb40 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -5,6 +5,7 @@ #include <cstring> #include "common/assert.h" #include "core/core.h" +#include "core/frontend/applets/controller.h" #include "core/frontend/applets/error.h" #include "core/frontend/applets/general_frontend.h" #include "core/frontend/applets/profile_select.h" @@ -15,6 +16,7 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/am/applets/controller.h" #include "core/hle/service/am/applets/error.h" #include "core/hle/service/am/applets/general_backend.h" #include "core/hle/service/am/applets/profile_select.h" @@ -140,14 +142,14 @@ void Applet::Initialize() { AppletFrontendSet::AppletFrontendSet() = default; -AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, +AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, + ErrorApplet error, ParentalControlsApplet parental_controls, PhotoViewer photo_viewer, ProfileSelect profile_select, - SoftwareKeyboard software_keyboard, WebBrowser web_browser, - ECommerceApplet e_commerce) - : parental_controls{std::move(parental_controls)}, error{std::move(error)}, - photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, - software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, - e_commerce{std::move(e_commerce)} {} + SoftwareKeyboard software_keyboard, WebBrowser web_browser) + : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)}, + parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)}, + profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)}, + web_browser{std::move(web_browser)} {} AppletFrontendSet::~AppletFrontendSet() = default; @@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { } void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { - if (set.parental_controls != nullptr) - frontend.parental_controls = std::move(set.parental_controls); - if (set.error != nullptr) + if (set.controller != nullptr) { + frontend.controller = std::move(set.controller); + } + + if (set.e_commerce != nullptr) { + frontend.e_commerce = std::move(set.e_commerce); + } + + if (set.error != nullptr) { frontend.error = std::move(set.error); - if (set.photo_viewer != nullptr) + } + + if (set.parental_controls != nullptr) { + frontend.parental_controls = std::move(set.parental_controls); + } + + if (set.photo_viewer != nullptr) { frontend.photo_viewer = std::move(set.photo_viewer); - if (set.profile_select != nullptr) + } + + if (set.profile_select != nullptr) { frontend.profile_select = std::move(set.profile_select); - if (set.software_keyboard != nullptr) + } + + if (set.software_keyboard != nullptr) { frontend.software_keyboard = std::move(set.software_keyboard); - if (set.web_browser != nullptr) + } + + if (set.web_browser != nullptr) { frontend.web_browser = std::move(set.web_browser); - if (set.e_commerce != nullptr) - frontend.e_commerce = std::move(set.e_commerce); + } } void AppletManager::SetDefaultAppletFrontendSet() { @@ -186,15 +205,24 @@ void AppletManager::SetDefaultAppletFrontendSet() { } void AppletManager::SetDefaultAppletsIfMissing() { - if (frontend.parental_controls == nullptr) { - frontend.parental_controls = - std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); + if (frontend.controller == nullptr) { + frontend.controller = + std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); + } + + if (frontend.e_commerce == nullptr) { + frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>(); } if (frontend.error == nullptr) { frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); } + if (frontend.parental_controls == nullptr) { + frontend.parental_controls = + std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); + } + if (frontend.photo_viewer == nullptr) { frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); } @@ -211,10 +239,6 @@ void AppletManager::SetDefaultAppletsIfMissing() { if (frontend.web_browser == nullptr) { frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); } - - if (frontend.e_commerce == nullptr) { - frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>(); - } } void AppletManager::ClearAll() { @@ -225,6 +249,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { switch (id) { case AppletId::Auth: return std::make_shared<Auth>(system, *frontend.parental_controls); + case AppletId::Controller: + return std::make_shared<Controller>(system, *frontend.controller); case AppletId::Error: return std::make_shared<Error>(system, *frontend.error); case AppletId::ProfileSelect: diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index e75be86a2..a1f4cf897 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -17,6 +17,7 @@ class System; } namespace Core::Frontend { +class ControllerApplet; class ECommerceApplet; class ErrorApplet; class ParentalControlsApplet; @@ -155,19 +156,20 @@ protected: }; struct AppletFrontendSet { - using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; + using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; + using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>; using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; + using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; - using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>; AppletFrontendSet(); - AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, - PhotoViewer photo_viewer, ProfileSelect profile_select, - SoftwareKeyboard software_keyboard, WebBrowser web_browser, - ECommerceApplet e_commerce); + AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error, + ParentalControlsApplet parental_controls, PhotoViewer photo_viewer, + ProfileSelect profile_select, SoftwareKeyboard software_keyboard, + WebBrowser web_browser); ~AppletFrontendSet(); AppletFrontendSet(const AppletFrontendSet&) = delete; @@ -176,13 +178,14 @@ struct AppletFrontendSet { AppletFrontendSet(AppletFrontendSet&&) noexcept; AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; - ParentalControlsApplet parental_controls; + ControllerApplet controller; + ECommerceApplet e_commerce; ErrorApplet error; + ParentalControlsApplet parental_controls; PhotoViewer photo_viewer; ProfileSelect profile_select; SoftwareKeyboard software_keyboard; WebBrowser web_browser; - ECommerceApplet e_commerce; }; class AppletManager { diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp new file mode 100644 index 000000000..2151da783 --- /dev/null +++ b/src/core/hle/service/am/applets/controller.cpp @@ -0,0 +1,210 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cstring> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/controller.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/controller.h" +#include "core/hle/service/hid/controllers/npad.h" + +namespace Service::AM::Applets { + +// This error code (0x183ACA) is thrown when the applet fails to initialize. +[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; +// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. +[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; + +static Core::Frontend::ControllerParameters ConvertToFrontendParameters( + ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, + std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { + HID::Controller_NPad::NPadType npad_style_set; + npad_style_set.raw = private_arg.style_set; + + return { + .min_players = std::max(s8(1), header.player_count_min), + .max_players = header.player_count_max, + .keep_controllers_connected = header.enable_take_over_connection, + .enable_single_mode = header.enable_single_mode, + .enable_border_color = header.enable_identification_color, + .border_colors = identification_colors, + .enable_explain_text = enable_text, + .explain_text = text, + .allow_pro_controller = npad_style_set.pro_controller == 1, + .allow_handheld = npad_style_set.handheld == 1, + .allow_dual_joycons = npad_style_set.joycon_dual == 1, + .allow_left_joycon = npad_style_set.joycon_left == 1, + .allow_right_joycon = npad_style_set.joycon_right == 1, + }; +} + +Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} + +Controller::~Controller() = default; + +void Controller::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_HID, "Initializing Controller Applet."); + + LOG_DEBUG(Service_HID, + "Initializing Applet with common_args: arg_version={}, lib_version={}, " + "play_startup_sound={}, size={}, system_tick={}, theme_color={}", + common_args.arguments_version, common_args.library_version, + common_args.play_startup_sound, common_args.size, common_args.system_tick, + common_args.theme_color); + + library_applet_version = LibraryAppletVersion{common_args.library_version}; + + const auto private_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(private_arg_storage != nullptr); + + const auto& private_arg = private_arg_storage->GetData(); + ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); + + std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate)); + ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), + "Unknown ControllerSupportArgPrivate revision={} with size={}", + library_applet_version, controller_private_arg.arg_private_size); + + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: { + const auto user_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(user_arg_storage != nullptr); + + const auto& user_arg = user_arg_storage->GetData(); + switch (library_applet_version) { + case LibraryAppletVersion::Version3: + case LibraryAppletVersion::Version4: + case LibraryAppletVersion::Version5: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); + std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld)); + break; + case LibraryAppletVersion::Version7: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", + library_applet_version, controller_private_arg.arg_size); + ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); + break; + } + break; + } + case ControllerSupportMode::ShowControllerStrapGuide: + case ControllerSupportMode::ShowControllerFirmwareUpdate: + default: { + UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); + break; + } + } +} + +bool Controller::TransactionComplete() const { + return complete; +} + +ResultCode Controller::GetStatus() const { + return status; +} + +void Controller::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void Controller::Execute() { + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: { + const auto parameters = [this] { + switch (library_applet_version) { + case LibraryAppletVersion::Version3: + case LibraryAppletVersion::Version4: + case LibraryAppletVersion::Version5: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_old.header, + controller_user_arg_old.enable_explain_text, + std::vector<IdentificationColor>( + controller_user_arg_old.identification_colors.begin(), + controller_user_arg_old.identification_colors.end()), + std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), + controller_user_arg_old.explain_text.end())); + case LibraryAppletVersion::Version7: + default: + return ConvertToFrontendParameters( + controller_private_arg, controller_user_arg_new.header, + controller_user_arg_new.enable_explain_text, + std::vector<IdentificationColor>( + controller_user_arg_new.identification_colors.begin(), + controller_user_arg_new.identification_colors.end()), + std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(), + controller_user_arg_new.explain_text.end())); + } + }(); + + is_single_mode = parameters.enable_single_mode; + + LOG_DEBUG(Service_HID, + "Controller Parameters: min_players={}, max_players={}, " + "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, " + "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, " + "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}", + parameters.min_players, parameters.max_players, + parameters.keep_controllers_connected, parameters.enable_single_mode, + parameters.enable_border_color, parameters.enable_explain_text, + parameters.allow_pro_controller, parameters.allow_handheld, + parameters.allow_dual_joycons, parameters.allow_left_joycon, + parameters.allow_right_joycon); + + frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters); + break; + } + case ControllerSupportMode::ShowControllerStrapGuide: + case ControllerSupportMode::ShowControllerFirmwareUpdate: + default: { + ConfigurationComplete(); + break; + } + } +} + +void Controller::ConfigurationComplete() { + ControllerSupportResultInfo result_info{}; + + const auto& players = Settings::values.players; + + // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. + // Otherwise, only count connected players from P1-P8. + result_info.player_count = + is_single_mode ? 1 + : static_cast<s8>(std::count_if( + players.begin(), players.end() - 2, + [](Settings::PlayerInput player) { return player.connected; })); + + result_info.selected_id = HID::Controller_NPad::IndexToNPad( + std::distance(players.begin(), + std::find_if(players.begin(), players.end(), + [](Settings::PlayerInput player) { return player.connected; }))); + + result_info.result = 0; + + LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}", + result_info.player_count, result_info.selected_id, result_info.result); + + complete = true; + out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo)); + std::memcpy(out_data.data(), &result_info, out_data.size()); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h new file mode 100644 index 000000000..f7bb3fba9 --- /dev/null +++ b/src/core/hle/service/am/applets/controller.h @@ -0,0 +1,123 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +using IdentificationColor = std::array<u8, 4>; +using ExplainText = std::array<char, 0x81>; + +enum class LibraryAppletVersion : u32_le { + Version3 = 0x3, // 1.0.0 - 2.3.0 + Version4 = 0x4, // 3.0.0 - 5.1.0 + Version5 = 0x5, // 6.0.0 - 7.0.1 + Version7 = 0x7, // 8.0.0+ +}; + +enum class ControllerSupportMode : u8 { + ShowControllerSupport = 0, + ShowControllerStrapGuide = 1, + ShowControllerFirmwareUpdate = 2, +}; + +enum class ControllerSupportCaller : u8 { + Application = 0, + System = 1, +}; + +struct ControllerSupportArgPrivate { + u32 arg_private_size{}; + u32 arg_size{}; + bool flag_0{}; + bool flag_1{}; + ControllerSupportMode mode{}; + ControllerSupportCaller caller{}; + u32 style_set{}; + u32 joy_hold_type{}; +}; +static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, + "ControllerSupportArgPrivate has incorrect size."); + +struct ControllerSupportArgHeader { + s8 player_count_min{}; + s8 player_count_max{}; + bool enable_take_over_connection{}; + bool enable_left_justify{}; + bool enable_permit_joy_dual{}; + bool enable_single_mode{}; + bool enable_identification_color{}; +}; +static_assert(sizeof(ControllerSupportArgHeader) == 0x7, + "ControllerSupportArgHeader has incorrect size."); + +// LibraryAppletVersion 0x3, 0x4, 0x5 +struct ControllerSupportArgOld { + ControllerSupportArgHeader header{}; + std::array<IdentificationColor, 4> identification_colors{}; + bool enable_explain_text{}; + std::array<ExplainText, 4> explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgOld) == 0x21C, + "ControllerSupportArgOld has incorrect size."); + +// LibraryAppletVersion 0x7 +struct ControllerSupportArgNew { + ControllerSupportArgHeader header{}; + std::array<IdentificationColor, 8> identification_colors{}; + bool enable_explain_text{}; + std::array<ExplainText, 8> explain_text{}; +}; +static_assert(sizeof(ControllerSupportArgNew) == 0x430, + "ControllerSupportArgNew has incorrect size."); + +struct ControllerSupportResultInfo { + s8 player_count{}; + INSERT_PADDING_BYTES(3); + u32 selected_id{}; + u32 result{}; +}; +static_assert(sizeof(ControllerSupportResultInfo) == 0xC, + "ControllerSupportResultInfo has incorrect size."); + +class Controller final : public Applet { +public: + explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_); + ~Controller() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ConfigurationComplete(); + +private: + const Core::Frontend::ControllerApplet& frontend; + + LibraryAppletVersion library_applet_version; + ControllerSupportArgPrivate controller_private_arg; + ControllerSupportArgOld controller_user_arg_old; + ControllerSupportArgNew controller_user_arg_new; + bool complete{false}; + ResultCode status{RESULT_SUCCESS}; + bool is_single_mode{false}; + std::vector<u8> out_data; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index d8359abaa..a2d3ded7b 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -26,7 +26,7 @@ namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, + explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params, const std::size_t instance_number) : ServiceFramework("IAudioRenderer") { // clang-format off @@ -94,14 +94,15 @@ private: void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); + std::vector<u8> output_params(ctx.GetWriteBufferSize()); + auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); - if (result.Succeeded()) { - ctx.WriteBuffer(result.Unwrap()); + if (result.IsSuccess()) { + ctx.WriteBuffer(output_params); } IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result.Code()); + rb.Push(result); } void Start(Kernel::HLERequestContext& ctx) { @@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { OpenAudioRendererImpl(ctx); } -static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { +static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { // +1 represents the final mix. return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + 1; @@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { constexpr u64 upsampler_manager_size = 0x48; // Calculates the part of the size that relates to mix buffers. - const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { // As of 8.0.0 this is the maximum on voice channels. constexpr u64 max_voice_channels = 6; @@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { // The size of the mixing info data structure. constexpr u64 mix_info_size = 0x940; @@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to voice channel info. - const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { constexpr u64 voice_info_size = 0x220; constexpr u64 voice_resource_size = 0xD0; @@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to memory pools. - const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); const u64 memory_pool_info_size = 0x20; return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); @@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { // Calculates the part of the size related to the splitter context. const auto calculate_splitter_context_size = - [](const AudioCore::AudioRendererParameter& params) -> u64 { + [](const AudioCommon::AudioRendererParameter& params) -> u64 { if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { return 0; } @@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to the upsampler info. - const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { - constexpr u64 upsampler_info_size = 0x280; - // Yes, using the buffer size over info alignment size is intentional here. - return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), - buffer_alignment_size); - }; + const auto calculate_upsampler_info_size = + [](const AudioCommon::AudioRendererParameter& params) { + constexpr u64 upsampler_info_size = 0x280; + // Yes, using the buffer size over info alignment size is intentional here. + return Common::AlignUp(upsampler_info_size * + (u64{params.submix_count} + params.sink_count), + buffer_alignment_size); + }; // Calculates the part of the size related to effect info. - const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { constexpr u64 effect_info_size = 0x2B0; return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); }; // Calculates the part of the size related to audio sink info. - const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { const u64 sink_info_size = 0x170; return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); }; // Calculates the part of the size related to voice state info. - const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { const u64 voice_state_size = 0x100; const u64 additional_size = buffer_alignment_size - 1; return Common::AlignUp(voice_state_size * params.voice_count + additional_size, @@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { + const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { // Extra size value appended to the end of the calculation. constexpr u64 appended = 128; @@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; + const auto calculate_command_buffer_size = + [](const AudioCommon::AudioRendererParameter& params) { + constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; + if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { + constexpr u64 command_buffer_size = 0x18000; - return command_buffer_size + alignment; - } + return command_buffer_size + alignment; + } - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. + // When the variadic command buffer is supported, this means + // the command generator for the audio renderer can issue commands + // that are (as one would expect), variable in size. So what we need to do + // is determine the maximum possible size for a few command data structures + // then multiply them by the amount of present commands indicated by the given + // respective audio parameters. - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; + constexpr u64 max_biquad_filters = 2; + constexpr u64 max_mix_buffers = 24; - constexpr u64 biquad_filter_command_size = 0x2C; + constexpr u64 biquad_filter_command_size = 0x2C; - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; + constexpr u64 depop_mix_command_size = 0x24; + constexpr u64 depop_setup_command_size = 0x50; - constexpr u64 effect_command_max_size = 0x540; + constexpr u64 effect_command_max_size = 0x540; - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; + constexpr u64 mix_command_size = 0x1C; + constexpr u64 mix_ramp_command_size = 0x24; + constexpr u64 mix_ramp_grouped_command_size = 0x13C; - constexpr u64 perf_command_size = 0x28; + constexpr u64 perf_command_size = 0x28; - constexpr u64 sink_command_size = 0x130; + constexpr u64 sink_command_size = 0x130; - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; + constexpr u64 submix_command_max_size = + depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; + constexpr u64 volume_command_size = 0x1C; + constexpr u64 volume_ramp_command_size = 0x20; - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + - mix_ramp_grouped_command_size); + constexpr u64 voice_biquad_filter_command_size = + biquad_filter_command_size * max_biquad_filters; + constexpr u64 voice_data_command_size = 0x9C; + const u64 voice_command_max_size = + (params.splitter_count * depop_setup_command_size) + + (voice_data_command_size + voice_biquad_filter_command_size + + volume_ramp_command_size + mix_ramp_grouped_command_size); - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + // Now calculate the individual elements that comprise the size and add them together. + const u64 effect_commands_size = params.effect_count * effect_command_max_size; - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + const u64 final_mix_commands_size = + depop_mix_command_size + volume_command_size * max_mix_buffers; - const u64 perf_commands_size = - perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const u64 perf_commands_size = + perf_command_size * + (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); - const u64 sink_commands_size = params.sink_count * sink_command_size; + const u64 sink_commands_size = params.sink_count * sink_command_size; - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + const u64 splitter_commands_size = + params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + const u64 submix_commands_size = params.submix_count * submix_command_max_size; - const u64 voice_commands_size = params.voice_count * voice_command_max_size; + const u64 voice_commands_size = params.voice_count * voice_command_max_size; - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; + return effect_commands_size + final_mix_commands_size + perf_commands_size + + sink_commands_size + splitter_commands_size + submix_commands_size + + voice_commands_size + alignment; + }; IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); + const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); u64 size = 0; size += calculate_mix_buffer_sizes(params); @@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); + const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index ca021a99f..3b6f7498e 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -196,7 +196,9 @@ private: const std::string& content_type_name) { if (client == nullptr) { client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT); - client->set_timeout_sec(timeout_seconds); + client->set_connection_timeout(timeout_seconds); + client->set_read_timeout(timeout_seconds); + client->set_write_timeout(timeout_seconds); } httplib::Headers headers{ @@ -255,7 +257,7 @@ private: return out; } - std::unique_ptr<httplib::Client> client; + std::unique_ptr<httplib::SSLClient> client; std::string path; u64 title_id; u64 build_id; @@ -443,13 +445,25 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global, std::map<std::string, EventStatus>& games) { httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)}; - client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS)); + client.set_connection_timeout(static_cast<int>(TIMEOUT_SECONDS)); + client.set_read_timeout(static_cast<int>(TIMEOUT_SECONDS)); + client.set_write_timeout(static_cast<int>(TIMEOUT_SECONDS)); httplib::Headers headers{ {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)}, }; + if (!client.is_valid()) { + LOG_ERROR(Service_BCAT, "Client is invalid, going offline!"); + return StatusResult::Offline; + } + + if (!client.is_socket_open()) { + LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!"); + return StatusResult::Offline; + } + const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers); if (response == nullptr) return StatusResult::Offline; diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index ab17a187e..a0ee116fa 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/caps/caps_c.h" namespace Service::Capture { @@ -47,7 +49,7 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") { static const FunctionInfo functions[] = { {1, nullptr, "CaptureRawImage"}, {2, nullptr, "CaptureRawImageWithTimeout"}, - {33, nullptr, "Unknown33"}, + {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"}, {1001, nullptr, "RequestTakingScreenShot"}, {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, {1011, nullptr, "NotifyTakingScreenShotRefused"}, @@ -72,4 +74,16 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") { CAPS_C::~CAPS_C() = default; +void CAPS_C::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto library_version{rp.Pop<u64>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", + library_version, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index a9d028689..b110301d4 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -16,6 +16,9 @@ class CAPS_C final : public ServiceFramework<CAPS_C> { public: explicit CAPS_C(); ~CAPS_C() override; + +private: + void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); }; } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index fffb2ecf9..e386470f7 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -25,7 +25,12 @@ CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") { CAPS_SU::~CAPS_SU() = default; void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Capture, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto library_version{rp.Pop<u64>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", + library_version, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index f36d8de2d..8e2b83629 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -31,8 +31,7 @@ public: CAPS_U::CAPS_U() : ServiceFramework("caps:u") { // clang-format off static const FunctionInfo functions[] = { - {31, nullptr, "GetShimLibraryVersion"}, - {32, nullptr, "SetShimLibraryVersion"}, + {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"}, {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, {103, nullptr, "DeleteAlbumContentsFileForApplication"}, {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, @@ -53,6 +52,18 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") { CAPS_U::~CAPS_U() = default; +void CAPS_U::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto library_version{rp.Pop<u64>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", + library_version, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index 689364de4..e04e56bbc 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -18,6 +18,7 @@ public: ~CAPS_U() override; private: + void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); }; 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( return FileSys::ERROR_ENTITY_NOT_FOUND; } - auto part = bis_factory->OpenPartitionStorage(id); + auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem()); if (part == nullptr) { return FileSys::ERROR_INVALID_ARGUMENT; } diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 26fd87f58..649128be4 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -844,8 +844,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { return; } - FileSys::StorageId id; - + FileSys::StorageId id{}; switch (parameters.space_id) { case FileSys::SaveDataSpaceId::NandUser: id = FileSys::StorageId::NandUser; @@ -857,6 +856,10 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { case FileSys::SaveDataSpaceId::NandSystem: id = FileSys::StorageId::NandSystem; break; + case FileSys::SaveDataSpaceId::TemporaryStorage: + case FileSys::SaveDataSpaceId::ProperSystem: + case FileSys::SaveDataSpaceId::SafeMode: + UNREACHABLE(); } auto filesystem = @@ -902,7 +905,14 @@ void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); - LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags); + LOG_WARNING(Service_FS, + "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n" + "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n" + "attribute.type={}, attribute.rank={}, attribute.index={}", + flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id, + parameters.attribute.user_id[1], parameters.attribute.user_id[0], + parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type), + static_cast<u32>(parameters.attribute.rank), parameters.attribute.index); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 8bc69c372..f47a9e61c 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -31,6 +31,10 @@ public: virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) = 0; + // When the controller is requesting a motion update for the shared memory + virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) {} + // Called when input devices should be loaded virtual void OnLoadInputDevices() = 0; diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 0b896d5ad..59b694cd4 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -42,8 +42,8 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, cur_entry.modifier = 0; if (Settings::values.keyboard_enabled) { for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { - cur_entry.key[i / KEYS_PER_BYTE] |= - (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); + auto& entry = cur_entry.key[i / KEYS_PER_BYTE]; + entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE))); } for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 0e7794dc7..e311bc18c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr std::size_t NPAD_OFFSET = 0x9A00; constexpr u32 BATTERY_FULL = 2; constexpr u32 MAX_NPAD_ID = 7; +constexpr std::size_t HANDHELD_INDEX = 8; constexpr std::array<u32, 10> npad_id_list{ 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, }; @@ -33,19 +34,41 @@ enum class JoystickId : std::size_t { Joystick_Right, }; -static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { +Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad( + Settings::ControllerType type) { switch (type) { case Settings::ControllerType::ProController: - return Controller_NPad::NPadControllerType::ProController; - case Settings::ControllerType::DualJoycon: - return Controller_NPad::NPadControllerType::JoyDual; + return NPadControllerType::ProController; + case Settings::ControllerType::DualJoyconDetached: + return NPadControllerType::JoyDual; case Settings::ControllerType::LeftJoycon: - return Controller_NPad::NPadControllerType::JoyLeft; + return NPadControllerType::JoyLeft; case Settings::ControllerType::RightJoycon: - return Controller_NPad::NPadControllerType::JoyRight; + return NPadControllerType::JoyRight; + case Settings::ControllerType::Handheld: + return NPadControllerType::Handheld; default: UNREACHABLE(); - return Controller_NPad::NPadControllerType::JoyDual; + return NPadControllerType::ProController; + } +} + +Settings::ControllerType Controller_NPad::MapNPadToSettingsType( + Controller_NPad::NPadControllerType type) { + switch (type) { + case NPadControllerType::ProController: + return Settings::ControllerType::ProController; + case NPadControllerType::JoyDual: + return Settings::ControllerType::DualJoyconDetached; + case NPadControllerType::JoyLeft: + return Settings::ControllerType::LeftJoycon; + case NPadControllerType::JoyRight: + return Settings::ControllerType::RightJoycon; + case NPadControllerType::Handheld: + return Settings::ControllerType::Handheld; + default: + UNREACHABLE(); + return Settings::ControllerType::ProController; } } @@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { case 6: case 7: return npad_id; - case 8: + case HANDHELD_INDEX: case NPAD_HANDHELD: - return 8; + return HANDHELD_INDEX; case 9: case NPAD_UNKNOWN: return 9; @@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { case 6: case 7: return static_cast<u32>(index); - case 8: + case HANDHELD_INDEX: return NPAD_HANDHELD; case 9: return NPAD_UNKNOWN; @@ -96,25 +119,35 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} Controller_NPad::~Controller_NPad() = default; -void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { +void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { const auto controller_type = connected_controllers[controller_idx].type; auto& controller = shared_memory_entries[controller_idx]; if (controller_type == NPadControllerType::None) { + styleset_changed_events[controller_idx].writable->Signal(); return; } controller.joy_styles.raw = 0; // Zero out controller.device_type.raw = 0; + controller.properties.raw = 0; switch (controller_type) { case NPadControllerType::None: UNREACHABLE(); break; + case NPadControllerType::ProController: + controller.joy_styles.pro_controller.Assign(1); + controller.device_type.pro_controller.Assign(1); + controller.properties.is_vertical.Assign(1); + controller.properties.use_plus.Assign(1); + controller.properties.use_minus.Assign(1); + controller.pad_assignment = NPadAssignments::Single; + break; case NPadControllerType::Handheld: controller.joy_styles.handheld.Assign(1); controller.device_type.handheld.Assign(1); - controller.pad_assignment = NPadAssignments::Dual; controller.properties.is_vertical.Assign(1); controller.properties.use_plus.Assign(1); controller.properties.use_minus.Assign(1); + controller.pad_assignment = NPadAssignments::Dual; break; case NPadControllerType::JoyDual: controller.joy_styles.joycon_dual.Assign(1); @@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { controller.device_type.pokeball.Assign(1); controller.pad_assignment = NPadAssignments::Single; break; - case NPadControllerType::ProController: - controller.joy_styles.pro_controller.Assign(1); - controller.device_type.pro_controller.Assign(1); - controller.properties.is_vertical.Assign(1); - controller.properties.use_plus.Assign(1); - controller.properties.use_minus.Assign(1); - controller.pad_assignment = NPadAssignments::Single; - break; } controller.single_color_error = ColorReadError::ReadOk; @@ -168,7 +193,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { controller.battery_level[0] = BATTERY_FULL; controller.battery_level[1] = BATTERY_FULL; controller.battery_level[2] = BATTERY_FULL; - styleset_changed_events[controller_idx].writable->Signal(); + + SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); } void Controller_NPad::OnInit() { @@ -192,36 +218,25 @@ void Controller_NPad::OnInit() { style.pokeball.Assign(1); } - std::transform( - Settings::values.players.begin(), Settings::values.players.end(), - connected_controllers.begin(), [](const Settings::PlayerInput& player) { - return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; - }); - - std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8, - [](const ControllerHolder& holder) { return holder.is_connected; }); + std::transform(Settings::values.players.begin(), Settings::values.players.end(), + connected_controllers.begin(), [](const Settings::PlayerInput& player) { + return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), + player.connected}; + }); // Account for handheld - if (connected_controllers[8].is_connected) - connected_controllers[8].type = NPadControllerType::Handheld; + if (connected_controllers[HANDHELD_INDEX].is_connected) { + connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; + } supported_npad_id_types.resize(npad_id_list.size()); std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), npad_id_list.size() * sizeof(u32)); - // Add a default dual joycon controller if none are present. - if (std::none_of(connected_controllers.begin(), connected_controllers.end(), - [](const ControllerHolder& controller) { return controller.is_connected; })) { - supported_npad_id_types.resize(npad_id_list.size()); - std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), - npad_id_list.size() * sizeof(u32)); - AddNewController(NPadControllerType::JoyDual); - } - for (std::size_t i = 0; i < connected_controllers.size(); ++i) { const auto& controller = connected_controllers[i]; if (controller.is_connected) { - AddNewControllerAt(controller.type, IndexToNPad(i)); + AddNewControllerAt(controller.type, i); } } } @@ -235,6 +250,9 @@ void Controller_NPad::OnLoadInputDevices() { std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); + std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, + players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, + motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); } } @@ -242,7 +260,7 @@ void Controller_NPad::OnRelease() {} void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { const auto controller_idx = NPadIdToIndex(npad_id); - [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type; + const auto controller_type = connected_controllers[controller_idx].type; if (!connected_controllers[controller_idx].is_connected) { return; } @@ -257,60 +275,70 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); using namespace Settings::NativeButton; - pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); - - pad_state.l_stick_right.Assign( - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( - Input::AnalogDirection::RIGHT)); - pad_state.l_stick_left.Assign( - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( - Input::AnalogDirection::LEFT)); - pad_state.l_stick_up.Assign( - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( - Input::AnalogDirection::UP)); - pad_state.l_stick_down.Assign( - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( - Input::AnalogDirection::DOWN)); - - pad_state.r_stick_right.Assign( - analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] - ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); - pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] - ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); - pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] - ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); - pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] - ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); - - pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); - pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); - - lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); - lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); - rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); - rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + if (controller_type != NPadControllerType::JoyLeft) { + pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.r_stick_right.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); + pad_state.r_stick_left.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); + pad_state.r_stick_up.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); + pad_state.r_stick_down.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); + rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); + rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); + } + + if (controller_type != NPadControllerType::JoyRight) { + pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); + + pad_state.l_stick_right.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); + pad_state.l_stick_left.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); + pad_state.l_stick_up.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); + pad_state.l_stick_down.Assign( + analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] + ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); + lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); + lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); + } + + if (controller_type == NPadControllerType::JoyLeft || + controller_type == NPadControllerType::JoyRight) { + pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); + pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); + } } void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t data_len) { - if (!IsControllerActivated()) + if (!IsControllerActivated()) { return; + } for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { auto& npad = shared_memory_entries[i]; const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, @@ -344,6 +372,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* continue; } const u32 npad_index = static_cast<u32>(i); + RequestPadStateUpdate(npad_index); auto& pad_state = npad_pad_states[npad_index]; @@ -360,13 +389,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; libnx_entry.connection_status.raw = 0; + libnx_entry.connection_status.IsConnected.Assign(1); switch (controller_type) { case NPadControllerType::None: UNREACHABLE(); break; + case NPadControllerType::ProController: + main_controller.connection_status.raw = 0; + main_controller.connection_status.IsConnected.Assign(1); + main_controller.connection_status.IsWired.Assign(1); + main_controller.pad.pad_states.raw = pad_state.pad_states.raw; + main_controller.pad.l_stick = pad_state.l_stick; + main_controller.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsWired.Assign(1); + break; case NPadControllerType::Handheld: handheld_entry.connection_status.raw = 0; + handheld_entry.connection_status.IsConnected.Assign(1); handheld_entry.connection_status.IsWired.Assign(1); handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); handheld_entry.connection_status.IsRightJoyConnected.Assign(1); @@ -375,57 +416,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; handheld_entry.pad.l_stick = pad_state.l_stick; handheld_entry.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsWired.Assign(1); + libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); + libnx_entry.connection_status.IsRightJoyConnected.Assign(1); + libnx_entry.connection_status.IsLeftJoyWired.Assign(1); + libnx_entry.connection_status.IsRightJoyWired.Assign(1); break; case NPadControllerType::JoyDual: dual_entry.connection_status.raw = 0; - + dual_entry.connection_status.IsConnected.Assign(1); dual_entry.connection_status.IsLeftJoyConnected.Assign(1); dual_entry.connection_status.IsRightJoyConnected.Assign(1); - dual_entry.connection_status.IsConnected.Assign(1); - - libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); - libnx_entry.connection_status.IsRightJoyConnected.Assign(1); - libnx_entry.connection_status.IsConnected.Assign(1); - dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; dual_entry.pad.l_stick = pad_state.l_stick; dual_entry.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); + libnx_entry.connection_status.IsRightJoyConnected.Assign(1); break; case NPadControllerType::JoyLeft: left_entry.connection_status.raw = 0; - left_entry.connection_status.IsConnected.Assign(1); + left_entry.connection_status.IsLeftJoyConnected.Assign(1); left_entry.pad.pad_states.raw = pad_state.pad_states.raw; left_entry.pad.l_stick = pad_state.l_stick; left_entry.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); break; case NPadControllerType::JoyRight: right_entry.connection_status.raw = 0; - right_entry.connection_status.IsConnected.Assign(1); + right_entry.connection_status.IsRightJoyConnected.Assign(1); right_entry.pad.pad_states.raw = pad_state.pad_states.raw; right_entry.pad.l_stick = pad_state.l_stick; right_entry.pad.r_stick = pad_state.r_stick; + + libnx_entry.connection_status.IsRightJoyConnected.Assign(1); break; case NPadControllerType::Pokeball: pokeball_entry.connection_status.raw = 0; - pokeball_entry.connection_status.IsConnected.Assign(1); - pokeball_entry.connection_status.IsWired.Assign(1); - pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; pokeball_entry.pad.l_stick = pad_state.l_stick; pokeball_entry.pad.r_stick = pad_state.r_stick; break; - case NPadControllerType::ProController: - main_controller.connection_status.raw = 0; - - main_controller.connection_status.IsConnected.Assign(1); - main_controller.connection_status.IsWired.Assign(1); - main_controller.pad.pad_states.raw = pad_state.pad_states.raw; - main_controller.pad.l_stick = pad_state.l_stick; - main_controller.pad.r_stick = pad_state.r_stick; - break; } // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate @@ -440,6 +476,131 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* shared_memory_entries.size() * sizeof(NPadEntry)); } +void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t data_len) { + if (!IsControllerActivated()) { + return; + } + for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { + auto& npad = shared_memory_entries[i]; + + const auto& controller_type = connected_controllers[i].type; + + if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { + continue; + } + + const std::array<SixAxisGeneric*, 6> controller_sixaxes{ + &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left, + &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right, + }; + + for (auto* sixaxis_sensor : controller_sixaxes) { + sixaxis_sensor->common.entry_count = 16; + sixaxis_sensor->common.total_entry_count = 17; + + const auto& last_entry = + sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; + + sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks(); + sixaxis_sensor->common.last_entry_index = + (sixaxis_sensor->common.last_entry_index + 1) % 17; + + auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; + + cur_entry.timestamp = last_entry.timestamp + 1; + cur_entry.timestamp2 = cur_entry.timestamp; + } + + // Try to read sixaxis sensor states + std::array<MotionDevice, 2> motion_devices; + + if (sixaxis_sensors_enabled && Settings::values.motion_enabled) { + sixaxis_at_rest = true; + for (std::size_t e = 0; e < motion_devices.size(); ++e) { + const auto& device = motions[i][e]; + if (device) { + std::tie(motion_devices[e].accel, motion_devices[e].gyro, + motion_devices[e].rotation, motion_devices[e].orientation) = + device->GetStatus(); + sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; + } + } + } + + auto& full_sixaxis_entry = + npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index]; + auto& handheld_sixaxis_entry = + npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index]; + auto& dual_left_sixaxis_entry = + npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index]; + auto& dual_right_sixaxis_entry = + npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index]; + auto& left_sixaxis_entry = + npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index]; + auto& right_sixaxis_entry = + npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index]; + + switch (controller_type) { + case NPadControllerType::None: + UNREACHABLE(); + break; + case NPadControllerType::ProController: + if (sixaxis_sensors_enabled && motions[i][0]) { + full_sixaxis_entry.accel = motion_devices[0].accel; + full_sixaxis_entry.gyro = motion_devices[0].gyro; + full_sixaxis_entry.rotation = motion_devices[0].rotation; + full_sixaxis_entry.orientation = motion_devices[0].orientation; + } + break; + case NPadControllerType::Handheld: + if (sixaxis_sensors_enabled && motions[i][0]) { + handheld_sixaxis_entry.accel = motion_devices[0].accel; + handheld_sixaxis_entry.gyro = motion_devices[0].gyro; + handheld_sixaxis_entry.rotation = motion_devices[0].rotation; + handheld_sixaxis_entry.orientation = motion_devices[0].orientation; + } + break; + case NPadControllerType::JoyDual: + if (sixaxis_sensors_enabled && motions[i][0]) { + // Set motion for the left joycon + dual_left_sixaxis_entry.accel = motion_devices[0].accel; + dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; + dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; + dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; + } + if (sixaxis_sensors_enabled && motions[i][1]) { + // Set motion for the right joycon + dual_right_sixaxis_entry.accel = motion_devices[1].accel; + dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; + dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; + dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; + } + break; + case NPadControllerType::JoyLeft: + if (sixaxis_sensors_enabled && motions[i][0]) { + left_sixaxis_entry.accel = motion_devices[0].accel; + left_sixaxis_entry.gyro = motion_devices[0].gyro; + left_sixaxis_entry.rotation = motion_devices[0].rotation; + left_sixaxis_entry.orientation = motion_devices[0].orientation; + } + break; + case NPadControllerType::JoyRight: + if (sixaxis_sensors_enabled && motions[i][1]) { + right_sixaxis_entry.accel = motion_devices[1].accel; + right_sixaxis_entry.gyro = motion_devices[1].gyro; + right_sixaxis_entry.rotation = motion_devices[1].rotation; + right_sixaxis_entry.orientation = motion_devices[1].orientation; + } + break; + case NPadControllerType::Pokeball: + break; + } + } + std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), + shared_memory_entries.size() * sizeof(NPadEntry)); +} + void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { style.raw = style_set.raw; } @@ -453,26 +614,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { supported_npad_id_types.clear(); supported_npad_id_types.resize(length / sizeof(u32)); std::memcpy(supported_npad_id_types.data(), data, length); - for (std::size_t i = 0; i < connected_controllers.size(); i++) { - auto& controller = connected_controllers[i]; - if (!controller.is_connected) { - continue; - } - const auto requested_controller = - i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type) - : NPadControllerType::Handheld; - if (!IsControllerSupported(requested_controller)) { - const auto is_handheld = requested_controller == NPadControllerType::Handheld; - if (is_handheld) { - controller.type = NPadControllerType::None; - controller.is_connected = false; - AddNewController(requested_controller); - } else { - controller.type = requested_controller; - InitNewlyAddedControler(i); - } - } - } } void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { @@ -492,6 +633,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { return hold_type; } +void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { + handheld_activation_mode = activation_mode; +} + +Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { + return handheld_activation_mode; +} + void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { const std::size_t npad_index = NPadIdToIndex(npad_id); ASSERT(npad_index < shared_memory_entries.size()); @@ -500,70 +649,86 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) } } -void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, +void Controller_NPad::VibrateController(const std::vector<u32>& controllers, const std::vector<Vibration>& vibrations) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); + LOG_TRACE(Service_HID, "called"); - if (!can_controllers_vibrate) { + if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { return; } - for (std::size_t i = 0; i < controller_ids.size(); i++) { - std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); - if (connected_controllers[controller_pos].is_connected) { - // TODO(ogniK): Vibrate the physical controller + bool success = true; + for (std::size_t i = 0; i < controllers.size(); ++i) { + if (!connected_controllers[i].is_connected) { + continue; + } + using namespace Settings::NativeButton; + const auto& button_state = buttons[i]; + if (button_state[A - BUTTON_HID_BEGIN]) { + if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( + vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, + vibrations[0].freq_low)) { + success = false; + } } } - last_processed_vibration = vibrations.back(); + if (success) { + last_processed_vibration = vibrations.back(); + } +} + +Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { + return last_processed_vibration; } std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { - // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should - // be signalled at least once, and signaled after a new controller is connected? const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; return styleset_event.readable; } -Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { - return last_processed_vibration; +void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { + styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal(); } -void Controller_NPad::AddNewController(NPadControllerType controller) { - controller = DecideBestController(controller); - if (controller == NPadControllerType::Handheld) { - connected_controllers[8] = {controller, true}; - InitNewlyAddedControler(8); - return; - } - const auto pos = - std::find_if(connected_controllers.begin(), connected_controllers.end() - 2, - [](const ControllerHolder& holder) { return !holder.is_connected; }); - if (pos == connected_controllers.end() - 2) { - LOG_ERROR(Service_HID, "Cannot connect any more controllers!"); +void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { + UpdateControllerAt(controller, npad_index, true); +} + +void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, + bool connected) { + if (!connected) { + DisconnectNPadAtIndex(npad_index); return; } - const auto controller_id = std::distance(connected_controllers.begin(), pos); - connected_controllers[controller_id] = {controller, true}; - InitNewlyAddedControler(controller_id); -} -void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) { - controller = DecideBestController(controller); if (controller == NPadControllerType::Handheld) { - connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; - InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); + Settings::values.players[HANDHELD_INDEX].controller_type = + MapNPadToSettingsType(controller); + Settings::values.players[HANDHELD_INDEX].connected = true; + connected_controllers[HANDHELD_INDEX] = {controller, true}; + InitNewlyAddedController(HANDHELD_INDEX); return; } - connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; - InitNewlyAddedControler(NPadIdToIndex(npad_id)); + Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); + Settings::values.players[npad_index].connected = true; + connected_controllers[npad_index] = {controller, true}; + InitNewlyAddedController(npad_index); } -void Controller_NPad::ConnectNPad(u32 npad_id) { - connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; +void Controller_NPad::DisconnectNPad(u32 npad_id) { + DisconnectNPadAtIndex(NPadIdToIndex(npad_id)); } -void Controller_NPad::DisconnectNPad(u32 npad_id) { - connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; +void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { + Settings::values.players[npad_index].connected = false; + connected_controllers[npad_index].is_connected = false; + + auto& controller = shared_memory_entries[npad_index]; + controller.joy_styles.raw = 0; // Zero out + controller.device_type.raw = 0; + controller.properties.raw = 0; + + SignalStyleSetChangedEvent(IndexToNPad(npad_index)); } void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { @@ -574,6 +739,30 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo return gyroscope_zero_drift_mode; } +bool Controller_NPad::IsSixAxisSensorAtRest() const { + return sixaxis_at_rest; +} + +void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { + sixaxis_sensors_enabled = six_axis_status; +} + +void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { + const auto npad_index_1 = NPadIdToIndex(npad_id_1); + const auto npad_index_2 = NPadIdToIndex(npad_id_2); + + // If the controllers at both npad indices form a pair of left and right joycons, merge them. + // Otherwise, do nothing. + if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && + connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || + (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && + connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { + // Disconnect the joycon at the second id and connect the dual joycon at the first index. + DisconnectNPad(npad_id_2); + AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); + } +} + void Controller_NPad::StartLRAssignmentMode() { // Nothing internally is used for lr assignment mode. Since we have the ability to set the // controller types from boot, it doesn't really matter about showing a selection screen @@ -599,8 +788,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); - InitNewlyAddedControler(npad_index_1); - InitNewlyAddedControler(npad_index_2); + AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1); + AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2); return true; } @@ -614,11 +803,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { case 0: return LedPattern{1, 0, 0, 0}; case 1: - return LedPattern{0, 1, 0, 0}; + return LedPattern{1, 1, 0, 0}; case 2: - return LedPattern{0, 0, 1, 0}; + return LedPattern{1, 1, 1, 0}; case 3: - return LedPattern{0, 0, 0, 1}; + return LedPattern{1, 1, 1, 1}; case 4: return LedPattern{1, 0, 0, 1}; case 5: @@ -628,11 +817,19 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { case 7: return LedPattern{0, 1, 1, 0}; default: - UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id); return LedPattern{0, 0, 0, 0}; } } +bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { + return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; +} + +void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, + u32 npad_id) { + unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; +} + void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { can_controllers_vibrate = can_vibrate; } @@ -651,13 +848,13 @@ void Controller_NPad::ClearAllConnectedControllers() { } void Controller_NPad::DisconnectAllConnectedControllers() { - for (ControllerHolder& controller : connected_controllers) { + for (auto& controller : connected_controllers) { controller.is_connected = false; } } void Controller_NPad::ConnectAllDisconnectedControllers() { - for (ControllerHolder& controller : connected_controllers) { + for (auto& controller : connected_controllers) { if (controller.type != NPadControllerType::None && !controller.is_connected) { controller.is_connected = true; } @@ -665,7 +862,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() { } void Controller_NPad::ClearAllControllers() { - for (ControllerHolder& controller : connected_controllers) { + for (auto& controller : connected_controllers) { controller.type = NPadControllerType::None; controller.is_connected = false; } @@ -713,92 +910,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const return false; } -Controller_NPad::NPadControllerType Controller_NPad::DecideBestController( - NPadControllerType priority) const { - if (IsControllerSupported(priority)) { - return priority; - } - const auto is_docked = Settings::values.use_docked_mode; - if (is_docked && priority == NPadControllerType::Handheld) { - priority = NPadControllerType::JoyDual; - if (IsControllerSupported(priority)) { - return priority; - } - } - std::vector<NPadControllerType> priority_list; - switch (priority) { - case NPadControllerType::ProController: - priority_list.push_back(NPadControllerType::JoyDual); - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::Pokeball); - break; - case NPadControllerType::Handheld: - priority_list.push_back(NPadControllerType::JoyDual); - priority_list.push_back(NPadControllerType::ProController); - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::Pokeball); - break; - case NPadControllerType::JoyDual: - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::ProController); - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::Pokeball); - break; - case NPadControllerType::JoyLeft: - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::JoyDual); - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::ProController); - priority_list.push_back(NPadControllerType::Pokeball); - break; - case NPadControllerType::JoyRight: - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyDual); - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::ProController); - priority_list.push_back(NPadControllerType::Pokeball); - break; - case NPadControllerType::Pokeball: - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::JoyDual); - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::ProController); - break; - default: - priority_list.push_back(NPadControllerType::JoyDual); - if (!is_docked) { - priority_list.push_back(NPadControllerType::Handheld); - } - priority_list.push_back(NPadControllerType::ProController); - priority_list.push_back(NPadControllerType::JoyLeft); - priority_list.push_back(NPadControllerType::JoyRight); - priority_list.push_back(NPadControllerType::JoyDual); - break; - } - - const auto iter = std::find_if(priority_list.begin(), priority_list.end(), - [this](auto type) { return IsControllerSupported(type); }); - if (iter == priority_list.end()) { - UNIMPLEMENTED_MSG("Could not find supported controller!"); - return priority; - } - - return *iter; -} - } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 5d4c58a43..fd5c5a6eb 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -32,6 +32,10 @@ public: // When the controller is requesting an update for the shared memory void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; + // When the controller is requesting a motion update for the shared memory + void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, + std::size_t size) override; + // Called when input devices should be loaded void OnLoadInputDevices() override; @@ -74,6 +78,12 @@ public: Single = 1, }; + enum class NpadHandheldActivationMode : u64 { + Dual = 0, + Single = 1, + None = 2, + }; + enum class NPadControllerType { None, ProController, @@ -110,22 +120,34 @@ public: void SetHoldType(NpadHoldType joy_hold_type); NpadHoldType GetHoldType() const; + void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); + NpadHandheldActivationMode GetNpadHandheldActivationMode() const; + void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); - void VibrateController(const std::vector<u32>& controller_ids, + void VibrateController(const std::vector<u32>& controllers, const std::vector<Vibration>& vibrations); - std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; Vibration GetLastVibration() const; - void AddNewController(NPadControllerType controller); - void AddNewControllerAt(NPadControllerType controller, u32 npad_id); + std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; + void SignalStyleSetChangedEvent(u32 npad_id) const; + + // Adds a new controller at an index. + void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); + // Adds a new controller at an index with connection status. + void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); - void ConnectNPad(u32 npad_id); void DisconnectNPad(u32 npad_id); + void DisconnectNPadAtIndex(std::size_t index); + void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; + bool IsSixAxisSensorAtRest() const; + void SetSixAxisEnabled(bool six_axis_status); LedPattern GetLedPattern(u32 npad_id); + bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; + void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); void SetVibrationEnabled(bool can_vibrate); bool IsVibrationEnabled() const; void ClearAllConnectedControllers(); @@ -133,6 +155,7 @@ public: void ConnectAllDisconnectedControllers(); void ClearAllControllers(); + void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); void StartLRAssignmentMode(); void StopLRAssignmentMode(); bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); @@ -141,6 +164,8 @@ public: // Specifically for cheat engine and other features. u32 GetAndResetPressState(); + static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); + static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); static std::size_t NPadIdToIndex(u32 npad_id); static u32 IndexToNPad(std::size_t index); @@ -244,6 +269,24 @@ private: }; static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); + struct SixAxisStates { + s64_le timestamp{}; + INSERT_PADDING_WORDS(2); + s64_le timestamp2{}; + Common::Vec3f accel{}; + Common::Vec3f gyro{}; + Common::Vec3f rotation{}; + std::array<Common::Vec3f, 3> orientation{}; + s64_le always_one{1}; + }; + static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); + + struct SixAxisGeneric { + CommonHeader common{}; + std::array<SixAxisStates, 17> sixaxis{}; + }; + static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); + enum class ColorReadError : u32_le { ReadOk = 0, ColorDoesntExist = 1, @@ -273,6 +316,13 @@ private: }; }; + struct MotionDevice { + Common::Vec3f accel; + Common::Vec3f gyro; + Common::Vec3f rotation; + std::array<Common::Vec3f, 3> orientation; + }; + struct NPadEntry { NPadType joy_styles; NPadAssignments pad_assignment; @@ -292,9 +342,12 @@ private: NPadGeneric pokeball_states; NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be // relying on this for the time being - INSERT_PADDING_BYTES( - 0x708 * - 6); // TODO(ogniK): SixAxis states, require more information before implementation + SixAxisGeneric sixaxis_full; + SixAxisGeneric sixaxis_handheld; + SixAxisGeneric sixaxis_dual_left; + SixAxisGeneric sixaxis_dual_right; + SixAxisGeneric sixaxis_left; + SixAxisGeneric sixaxis_right; NPadDevice device_type; NPadProperties properties; INSERT_PADDING_WORDS(1); @@ -309,31 +362,38 @@ private: bool is_connected; }; - void InitNewlyAddedControler(std::size_t controller_idx); + void InitNewlyAddedController(std::size_t controller_idx); bool IsControllerSupported(NPadControllerType controller) const; - NPadControllerType DecideBestController(NPadControllerType priority) const; void RequestPadStateUpdate(u32 npad_id); u32 press_state{}; NPadType style{}; std::array<NPadEntry, 10> shared_memory_entries{}; - std::array< + using ButtonArray = std::array< std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, - 10> - buttons; - std::array< + 10>; + using StickArray = std::array< std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, - 10> - sticks; + 10>; + using MotionArray = std::array< + std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>, + 10>; + ButtonArray buttons; + StickArray sticks; + MotionArray motions; std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; + NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; // Each controller should have their own styleset changed event std::array<Kernel::EventPair, 10> styleset_changed_events; Vibration last_processed_vibration{}; std::array<ControllerHolder, 10> connected_controllers{}; + std::array<bool, 10> unintended_home_button_input_protection{}; GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; bool can_controllers_vibrate{true}; + bool sixaxis_sensors_enabled{true}; + bool sixaxis_at_rest{true}; std::array<ControllerPad, 10> npad_pad_states{}; bool is_in_lr_assignment_mode{false}; Core::System& system; diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index e326f8f5c..0df395e85 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -40,9 +40,14 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin cur_entry.sampling_number = last_entry.sampling_number + 1; cur_entry.sampling_number2 = cur_entry.sampling_number; - const auto [x, y, pressed] = touch_device->GetStatus(); + bool pressed = false; + float x, y; + std::tie(x, y, pressed) = touch_device->GetStatus(); auto& touch_entry = cur_entry.states[0]; touch_entry.attribute.raw = 0; + if (!pressed && touch_btn_device) { + std::tie(x, y, pressed) = touch_btn_device->GetStatus(); + } if (pressed && Settings::values.touchscreen.enabled) { touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); @@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin void Controller_Touchscreen::OnLoadInputDevices() { touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); + if (Settings::values.use_touch_from_button) { + touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); + } else { + touch_btn_device.reset(); + } } } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index a1d97269e..4d9042adc 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -68,6 +68,7 @@ private: "TouchScreenSharedMemory is an invalid size"); TouchScreenSharedMemory shared_memory{}; std::unique_ptr<Input::TouchDevice> touch_device; + std::unique_ptr<Input::TouchDevice> touch_btn_device; s64_le last_touch{}; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1e95b7580..8918946a1 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -38,11 +38,10 @@ namespace Service::HID { // Updating period for each HID device. -// TODO(ogniK): Find actual polling rate of hid -constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66}; -[[maybe_unused]] constexpr auto accelerometer_update_ns = - std::chrono::nanoseconds{1000000000 / 100}; -[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100}; +// HID is polled every 15ms, this value was derived from +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet +constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) +constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; IAppletResource::IAppletResource(Core::System& system) @@ -81,10 +80,14 @@ IAppletResource::IAppletResource(Core::System& system) [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { UpdateControllers(user_data, ns_late); }); - - // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) + motion_update_event = Core::Timing::CreateEvent( + "HID::MotionPadCallback", + [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + UpdateMotion(user_data, ns_late); + }); system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); + system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); ReloadInputDevices(); } @@ -124,6 +127,16 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } +void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + for (const auto& controller : controllers) { + controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); + } + + core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); +} + class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { public: IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") { @@ -166,8 +179,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {56, nullptr, "ActivateJoyXpad"}, {58, nullptr, "GetJoyXpadLifoHandle"}, {59, nullptr, "GetJoyXpadIds"}, - {60, nullptr, "ActivateSixAxisSensor"}, - {61, nullptr, "DeactivateSixAxisSensor"}, + {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, + {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, {62, nullptr, "GetSixAxisSensorLifoHandle"}, {63, nullptr, "ActivateJoySixAxisSensor"}, {64, nullptr, "DeactivateJoySixAxisSensor"}, @@ -175,7 +188,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, {68, nullptr, "IsSixAxisSensorFusionEnabled"}, - {69, nullptr, "EnableSixAxisSensorFusion"}, + {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, {70, nullptr, "SetSixAxisSensorFusionParameters"}, {71, nullptr, "GetSixAxisSensorFusionParameters"}, {72, nullptr, "ResetSixAxisSensorFusionParameters"}, @@ -211,8 +224,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, - {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, - {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, + {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, + {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, {135, nullptr, "SetNpadCaptureButtonAssignment"}, @@ -331,6 +344,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { rb.Push(0); } +void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); + + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -435,6 +473,19 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + [[maybe_unused]] const auto enable{rp.Pop<bool>()}; + const auto handle{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto handle{rp.Pop<u32>()}; @@ -486,13 +537,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { const auto handle{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. - rb.Push(true); + rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .IsSixAxisSensorAtRest()); } void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { @@ -673,13 +724,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto unknown_1{rp.Pop<u32>()}; - const auto unknown_2{rp.Pop<u32>()}; + const auto npad_id_1{rp.Pop<u32>()}; + const auto npad_id_2{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, - "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", - unknown_1, unknown_2, applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -714,8 +767,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { const auto applet_resource_user_id{rp.Pop<u64>()}; const auto mode{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", - applet_resource_user_id, mode); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, + mode); + + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -725,11 +781,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - IPC::ResponseBuilder rb{ctx, 2}; + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); + rb.Push<u64>( + static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) + .GetNpadHandheldActivationMode())); } void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { @@ -751,6 +809,40 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { } } +void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, + applet_resource_user_id); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<bool>(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); +} + +void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto unintended_home_button_input_protection{rp.Pop<bool>()}; + const auto npad_id{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," + "applet_resource_user_id={}", + npad_id, unintended_home_button_input_protection, applet_resource_user_id); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.SetUnintendedHomeButtonInputProtectionEnabled( + unintended_home_button_input_protection, npad_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -772,18 +864,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto controller_id{rp.Pop<u32>()}; + const auto controller{rp.Pop<u32>()}; const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, + LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); applet_resource->GetController<Controller_NPad>(HidController::NPad) - .VibrateController({controller_id}, {vibration_values}); + .VibrateController({controller}, {vibration_values}); } void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { @@ -801,8 +893,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { std::memcpy(controller_list.data(), controllers.data(), controllers.size()); std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); - std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(), - [](u32 controller_id) { return controller_id - 3; }); applet_resource->GetController<Controller_NPad>(HidController::NPad) .VibrateController(controller_list, vibration_list); @@ -845,8 +935,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_vibrate{rp.Pop<bool>()}; - applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetVibrationEnabled(can_vibrate); + Settings::values.vibration_enabled = can_vibrate; LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); @@ -859,8 +948,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push( - applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled()); + rb.Push(Settings::values.vibration_enabled); } void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index efb07547f..fd0372b18 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -65,10 +65,12 @@ private: void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); std::shared_ptr<Kernel::SharedMemory> shared_mem; std::shared_ptr<Core::Timing::EventType> pad_update_event; + std::shared_ptr<Core::Timing::EventType> motion_update_event; Core::System& system; std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> @@ -86,6 +88,8 @@ private: void CreateAppletResource(Kernel::HLERequestContext& ctx); void ActivateXpad(Kernel::HLERequestContext& ctx); void GetXpadIDs(Kernel::HLERequestContext& ctx); + void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); + void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); void ActivateDebugPad(Kernel::HLERequestContext& ctx); void ActivateTouchScreen(Kernel::HLERequestContext& ctx); void ActivateMouse(Kernel::HLERequestContext& ctx); @@ -95,6 +99,7 @@ private: void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); void StartSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); + void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); @@ -118,6 +123,8 @@ private: void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); void SwapNpadAssignment(Kernel::HLERequestContext& ctx); + void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); + void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); void SendVibrationValue(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp index 4730070cb..d73b90015 100644 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/manager.cpp @@ -131,7 +131,7 @@ template <typename T> T GetRandomValue(T min, T max) { std::random_device device; std::mt19937 gen(device()); - std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max)); + std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max)); return static_cast<T>(distribution(gen)); } @@ -428,7 +428,7 @@ bool MiiManager::IsFullDatabase() const { } u32 MiiManager::GetCount(SourceFlag source_flag) const { - u32 count{}; + std::size_t count{}; if ((source_flag & SourceFlag::Database) != SourceFlag::None) { // TODO(bunnei): We don't implement the Mii database, but when we do, update this count += 0; @@ -436,7 +436,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const { if ((source_flag & SourceFlag::Default) != SourceFlag::None) { count += DefaultMiiCount; } - return count; + return static_cast<u32>(count); } ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, 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 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <atomic> #include "common/logging/log.h" @@ -72,10 +73,10 @@ private: std::array<u8, 10> uuid; u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it // mean something else - INSERT_PADDING_BYTES(0x15); + std::array<u8, 0x15> padding_1; u32_le protocol; u32_le tag_type; - INSERT_PADDING_BYTES(0x2c); + std::array<u8, 0x2c> padding_2; }; static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); @@ -213,13 +214,15 @@ private: LOG_DEBUG(Service_NFP, "called"); IPC::ResponseBuilder rb{ctx, 2}; - auto amiibo = nfp_interface.GetAmiiboBuffer(); - TagInfo tag_info{}; - tag_info.uuid = amiibo.uuid; - tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); - - tag_info.protocol = 1; // TODO(ogniK): Figure out actual values - tag_info.tag_type = 2; + const auto& amiibo = nfp_interface.GetAmiiboBuffer(); + const TagInfo tag_info{ + .uuid = amiibo.uuid, + .uuid_length = static_cast<u8>(tag_info.uuid.size()), + .padding_1 = {}, + .protocol = 1, // TODO(ogniK): Figure out actual values + .tag_type = 2, + .padding_2 = {}, + }; ctx.WriteBuffer(tag_info); rb.Push(RESULT_SUCCESS); } @@ -236,7 +239,7 @@ private: LOG_DEBUG(Service_NFP, "called"); IPC::ResponseBuilder rb{ctx, 2}; - auto amiibo = nfp_interface.GetAmiiboBuffer(); + const auto& amiibo = nfp_interface.GetAmiiboBuffer(); ctx.WriteBuffer(amiibo.model_info); rb.Push(RESULT_SUCCESS); } diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 01ddcdbd6..2e9d95195 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/service.h" +#include "core/network/network.h" #include "core/settings.h" namespace Service::NIFM { @@ -174,6 +175,16 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + const auto [ipv4, error] = Network::GetHostIPv4Address(); + UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(ipv4); + } void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NIFM, "called"); @@ -235,7 +246,7 @@ IGeneralService::IGeneralService(Core::System& system) {9, nullptr, "SetNetworkProfile"}, {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, {11, nullptr, "GetScanDataOld"}, - {12, nullptr, "GetCurrentIpAddress"}, + {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"}, {13, nullptr, "GetCurrentAccessPointOld"}, {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, {15, nullptr, "GetCurrentIpConfigInfo"}, diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 886450be2..58ee1f712 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -5,6 +5,7 @@ #include "common/logging/log.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/errors.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index d4ba88147..f2529a12e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -46,6 +46,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: return GetVARegions(input, output); case IoctlCommand::IocUnmapBufferCommand: return UnmapBuffer(input, output); + case IoctlCommand::IocFreeSpaceCommand: + return FreeSpace(input, output); default: break; } @@ -91,6 +93,20 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& return result; } +u32 nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlFreeSpace params{}; + std::memcpy(¶ms, input.data(), input.size()); + + LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset, + params.pages, params.page_size); + + system.GPU().MemoryManager().Unmap(params.offset, + static_cast<std::size_t>(params.pages) * params.page_size); + + std::memcpy(output.data(), ¶ms, output.size()); + return NvErrCodes::Success; +} + u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { const auto num_entries = input.size() / sizeof(IoctlRemapEntry); @@ -265,7 +281,7 @@ std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gp } } - return {}; + return std::nullopt; } void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, @@ -286,7 +302,7 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { return size; } - return {}; + return std::nullopt; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index 9a0cdff0c..fcdb40d93 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -82,6 +82,7 @@ private: IocBindChannelCommand = 0x40044101, IocGetVaRegionsCommand = 0xC0404108, IocUnmapBufferCommand = 0xC0084105, + IocFreeSpaceCommand = 0xC0104103, }; struct IoctlInitalizeEx { @@ -107,6 +108,13 @@ private: }; static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + struct IoctlFreeSpace { + u64_le offset; + u32_le pages; + u32_le page_size; + }; + static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size"); + struct IoctlRemapEntry { u16_le flags; u16_le kind; @@ -162,6 +170,7 @@ private: u32 Remap(const std::vector<u8>& input, std::vector<u8>& output); u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + u32 FreeSpace(const std::vector<u8>& input, std::vector<u8>& output); u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index bdae8b887..fcb612864 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -22,6 +22,18 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std:: switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocSetNVMAPfdCommand: return SetNVMAPfd(input, output); + case IoctlCommand::IocSubmit: + return Submit(input, output); + case IoctlCommand::IocGetSyncpoint: + return GetSyncpoint(input, output); + case IoctlCommand::IocGetWaitbase: + return GetWaitbase(input, output); + case IoctlCommand::IocMapBuffer: + return MapBuffer(input, output); + case IoctlCommand::IocMapBufferEx: + return MapBufferEx(input, output); + case IoctlCommand::IocUnmapBufferEx: + return UnmapBufferEx(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); @@ -30,11 +42,67 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std:: u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; - std::memcpy(¶ms, input.data(), input.size()); + std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; return 0; } +u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSubmit params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); + return 0; +} + +u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetSyncpoint params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); + LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + params.value = 0; // Seems to be hard coded at 0 + std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); + return 0; +} + +u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetWaitbase params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); + LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + params.value = 0; // Seems to be hard coded at 0 + std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); + return 0; +} + +u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, + params.address_1); + params.address_1 = 0; + params.address_2 = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); + return 0; +} + +u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBufferEx params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, + params.address_1); + params.address_1 = 0; + params.address_2 = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx)); + return 0; +} + +u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlUnmapBufferEx params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx)); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index cbdac8069..4332db118 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -23,16 +23,66 @@ public: private: enum class IoctlCommand : u32_le { IocSetNVMAPfdCommand = 0x40044801, + IocSubmit = 0xC0400001, + IocGetSyncpoint = 0xC0080002, + IocGetWaitbase = 0xC0080003, + IocMapBuffer = 0xC01C0009, + IocMapBufferEx = 0xC0A40009, + IocUnmapBufferEx = 0xC0A4000A, }; struct IoctlSetNvmapFD { u32_le nvmap_fd; }; - static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); + static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size"); + + struct IoctlSubmit { + INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size"); + + struct IoctlGetSyncpoint { + u32 unknown; // seems to be ignored? Nintendo added this + u32 value; + }; + static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size"); + + struct IoctlGetWaitbase { + u32 unknown; // seems to be ignored? Nintendo added this + u32 value; + }; + static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size"); + + struct IoctlMapBuffer { + u32 unknown; + u32 address_1; + u32 address_2; + INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size"); + + struct IoctlMapBufferEx { + u32 unknown; + u32 address_1; + u32 address_2; + INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size"); + + struct IoctlUnmapBufferEx { + INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size"); u32_le nvmap_fd{}; u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index c695b8863..9da19ad56 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -22,6 +22,18 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocSetNVMAPfdCommand: return SetNVMAPfd(input, output); + case IoctlCommand::IocSubmit: + return Submit(input, output); + case IoctlCommand::IocGetSyncpoint: + return GetSyncpoint(input, output); + case IoctlCommand::IocGetWaitbase: + return GetWaitbase(input, output); + case IoctlCommand::IocMapBuffer: + return MapBuffer(input, output); + case IoctlCommand::IocMapBufferEx: + return MapBuffer(input, output); + case IoctlCommand::IocUnmapBufferEx: + return UnmapBufferEx(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); @@ -30,11 +42,71 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { IoctlSetNvmapFD params{}; - std::memcpy(¶ms, input.data(), input.size()); + std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); nvmap_fd = params.nvmap_fd; return 0; } +u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlSubmit params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + // Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU + params.command_buffer = {}; + + std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); + return 0; +} + +u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetSyncpoint params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); + LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + params.value = 0; // Seems to be hard coded at 0 + std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); + return 0; +} + +u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlGetWaitbase params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); + LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); + params.value = 0; // Seems to be hard coded at 0 + std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); + return 0; +} + +u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBuffer params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, + params.address_1); + params.address_1 = 0; + params.address_2 = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); + return 0; +} + +u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlMapBufferEx params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, + params.address_1); + params.address_1 = 0; + params.address_2 = 0; + std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx)); + return 0; +} + +u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { + IoctlUnmapBufferEx params{}; + std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx)); + return 0; +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index bec32bea1..a7bb7bbd5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -4,6 +4,7 @@ #pragma once +#include <array> #include <vector> #include "common/common_types.h" #include "common/swap.h" @@ -23,6 +24,12 @@ public: private: enum class IoctlCommand : u32_le { IocSetNVMAPfdCommand = 0x40044801, + IocSubmit = 0xC0400001, + IocGetSyncpoint = 0xC0080002, + IocGetWaitbase = 0xC0080003, + IocMapBuffer = 0xC01C0009, + IocMapBufferEx = 0xC03C0009, + IocUnmapBufferEx = 0xC03C000A, }; struct IoctlSetNvmapFD { @@ -30,9 +37,65 @@ private: }; static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); + struct IoctlSubmitCommandBuffer { + u32 id; + u32 offset; + u32 count; + }; + static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC, + "IoctlSubmitCommandBuffer is incorrect size"); + + struct IoctlSubmit { + u32 command_buffer_count; + u32 relocations_count; + u32 syncpt_count; + u32 wait_count; + std::array<IoctlSubmitCommandBuffer, 4> command_buffer; + }; + static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size"); + + struct IoctlGetSyncpoint { + u32 unknown; // seems to be ignored? Nintendo added this + u32 value; + }; + static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size"); + + struct IoctlGetWaitbase { + u32 unknown; // seems to be ignored? Nintendo added this + u32 value; + }; + static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size"); + + struct IoctlMapBuffer { + u32 unknown; + u32 address_1; + u32 address_2; + INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size"); + + struct IoctlMapBufferEx { + u32 unknown; + u32 address_1; + u32 address_2; + INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size"); + + struct IoctlUnmapBufferEx { + INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure + }; + static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size"); + u32_le nvmap_fd{}; u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); + u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); + u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); + u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index d7a1bef91..7706a5590 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -54,7 +54,7 @@ struct EventInterface { } mask = mask >> 1; } - return {}; + return std::nullopt; } void SetEventStatus(const u32 event_id, EventState new_status) { EventState old_status = status[event_id]; diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 637b310d7..4f1e210b1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -99,6 +99,20 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, queue_sequence.push_back(slot); } +void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) { + const auto itr = std::find_if(queue.begin(), queue.end(), + [slot](const Buffer& buffer) { return buffer.slot == slot; }); + ASSERT(itr != queue.end()); + ASSERT(itr->status != Buffer::Status::Free); + itr->status = Buffer::Status::Free; + itr->multi_fence = multi_fence; + itr->swap_interval = 0; + + free_buffers.push_back(slot); + + buffer_wait_event.writable->Signal(); +} + std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { auto itr = queue.end(); // Iterate to find a queued buffer matching the requested slot. diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 8a837e5aa..e7517c7e1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -95,6 +95,7 @@ public: void QueueBuffer(u32 slot, BufferTransformFlags transform, const Common::Rectangle<int>& crop_rect, u32 swap_interval, Service::Nvidia::MultiFence& multi_fence); + void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence); std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); void Disconnect(); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index f644a460d..c64673dba 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -114,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { [&](const VI::Display& display) { return display.GetName() == name; }); if (itr == displays.end()) { - return {}; + return std::nullopt; } return itr->GetID(); @@ -124,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { auto* const display = FindDisplay(display_id); if (display == nullptr) { - return {}; + return std::nullopt; } const u64 layer_id = next_layer_id++; @@ -144,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co const auto* const layer = FindLayer(display_id, layer_id); if (layer == nullptr) { - return {}; + return std::nullopt; } return layer->GetBufferQueue().GetId(); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index fa5347af9..ba9159ee0 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -89,8 +89,6 @@ namespace Service { return function_string; } -//////////////////////////////////////////////////////////////////////////////////////////////////// - ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker) : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} @@ -105,10 +103,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) port_installed = true; } -void ServiceFrameworkBase::InstallAsNamedPort() { +void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { ASSERT(!port_installed); - auto& kernel = Core::System::GetInstance().Kernel(); auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); server_port->SetHleHandler(shared_from_this()); @@ -116,10 +113,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() { port_installed = true; } -std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { +std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { ASSERT(!port_installed); - auto& kernel = Core::System::GetInstance().Kernel(); auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); auto port = MakeResult(std::move(server_port)).Unwrap(); @@ -191,9 +187,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co return RESULT_SUCCESS; } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Module interface - /// Initialize ServiceManager void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it @@ -246,7 +239,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { PSC::InstallInterfaces(*sm); PSM::InstallInterfaces(*sm); Set::InstallInterfaces(*sm); - Sockets::InstallInterfaces(*sm); + Sockets::InstallInterfaces(*sm, system); SPL::InstallInterfaces(*sm); SSL::InstallInterfaces(*sm); Time::InstallInterfaces(system); 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: /// Creates a port pair and registers this service with the given ServiceManager. void InstallAsService(SM::ServiceManager& service_manager); /// Creates a port pair and registers it on the kernel's global port registry. - void InstallAsNamedPort(); + void InstallAsNamedPort(Kernel::KernelCore& kernel); /// Creates and returns an unregistered port for the service. - std::shared_ptr<Kernel::ClientPort> CreatePort(); + std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel); void InvokeRequest(Kernel::HLERequestContext& ctx); 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); constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); -ServiceManager::ServiceManager() = default; +ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} ServiceManager::~ServiceManager() = default; void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { @@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { } static ResultCode ValidateServiceName(const std::string& name) { - if (name.size() <= 0 || name.size() > 8) { + if (name.empty() || name.size() > 8) { LOG_ERROR(Service_SM, "Invalid service name! service={}", name); return ERR_INVALID_NAME; } - if (name.find('\0') != std::string::npos) { + if (name.rfind('\0') != std::string::npos) { LOG_ERROR(Service_SM, "A non null terminated service was passed"); return ERR_INVALID_NAME; } @@ -43,13 +43,13 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self, ASSERT(self->sm_interface.expired()); auto sm = std::make_shared<SM>(self, kernel); - sm->InstallAsNamedPort(); + sm->InstallAsNamedPort(kernel); self->sm_interface = sm; self->controller_interface = std::make_unique<Controller>(); } -ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( - std::string name, unsigned int max_sessions) { +ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name, + u32 max_sessions) { CASCADE_CODE(ValidateServiceName(name)); @@ -58,7 +58,6 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( return ERR_ALREADY_REGISTERED; } - auto& kernel = Core::System::GetInstance().Kernel(); auto [server_port, client_port] = Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); 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 { public: static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); - ServiceManager(); + explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name, - unsigned int max_sessions); + u32 max_sessions); ResultCode UnregisterService(const std::string& name); ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); @@ -79,6 +79,9 @@ private: /// Map of registered services, retrieved using GetServicePort or ConnectToService. std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; + + /// Kernel context + Kernel::KernelCore& kernel; }; } // namespace Service::SM diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h new file mode 100644 index 000000000..2d53e52b6 --- /dev/null +++ b/src/core/hle/service/sockets/blocking_worker.h @@ -0,0 +1,161 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <memory> +#include <string> +#include <string_view> +#include <thread> +#include <variant> +#include <vector> + +#include <fmt/format.h> + +#include "common/assert.h" +#include "common/microprofile.h" +#include "common/thread.h" +#include "core/core.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/writable_event.h" + +namespace Service::Sockets { + +/** + * Worker abstraction to execute blocking calls on host without blocking the guest thread + * + * @tparam Service Service where the work is executed + * @tparam Types Types of work to execute + */ +template <class Service, class... Types> +class BlockingWorker { + using This = BlockingWorker<Service, Types...>; + using WorkVariant = std::variant<std::monostate, Types...>; + +public: + /// Create a new worker + static std::unique_ptr<This> Create(Core::System& system, Service* service, + std::string_view name) { + return std::unique_ptr<This>(new This(system, service, name)); + } + + ~BlockingWorker() { + while (!is_available.load(std::memory_order_relaxed)) { + // Busy wait until work is finished + std::this_thread::yield(); + } + // Monostate means to exit the thread + work = std::monostate{}; + work_event.Set(); + thread.join(); + } + + /** + * Try to capture the worker to send work after a success + * @returns True when the worker has been successfully captured + */ + bool TryCapture() { + bool expected = true; + return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed, + std::memory_order_relaxed); + } + + /** + * Send work to this worker abstraction + * @see TryCapture must be called before attempting to call this function + */ + template <class Work> + void SendWork(Work new_work) { + ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured"); + work = std::move(new_work); + work_event.Set(); + } + + /// Generate a callback for @see SleepClientThread + template <class Work> + auto Callback() { + return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + ASSERT(reason == Kernel::ThreadWakeupReason::Signal); + std::get<Work>(work).Response(ctx); + is_available.store(true); + }; + } + + /// Get kernel event that will be signalled by the worker when the host operation finishes + std::shared_ptr<Kernel::WritableEvent> KernelEvent() const { + return kernel_event; + } + +private: + explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) { + auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name)); + kernel_event = std::move(pair.writable); + thread = std::thread([this, &system, service, name] { Run(system, service, name); }); + } + + void Run(Core::System& system, Service* service, std::string_view name) { + system.RegisterHostThread(); + + const std::string thread_name = fmt::format("yuzu:{}", name); + MicroProfileOnThreadCreate(thread_name.c_str()); + Common::SetCurrentThreadName(thread_name.c_str()); + + bool keep_running = true; + while (keep_running) { + work_event.Wait(); + + const auto visit_fn = [service, &keep_running]<typename T>(T&& w) { + if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) { + keep_running = false; + } else { + w.Execute(service); + } + }; + std::visit(visit_fn, work); + + kernel_event->Signal(); + } + } + + std::thread thread; + WorkVariant work; + Common::Event work_event; + std::shared_ptr<Kernel::WritableEvent> kernel_event; + std::atomic_bool is_available{true}; +}; + +template <class Service, class... Types> +class BlockingWorkerPool { + using Worker = BlockingWorker<Service, Types...>; + +public: + explicit BlockingWorkerPool(Core::System& system_, Service* service_) + : system{system_}, service{service_} {} + + /// Returns a captured worker thread, creating new ones if necessary + Worker* CaptureWorker() { + for (auto& worker : workers) { + if (worker->TryCapture()) { + return worker.get(); + } + } + auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size())); + [[maybe_unused]] const bool success = new_worker->TryCapture(); + ASSERT(success); + + return workers.emplace_back(std::move(new_worker)).get(); + } + +private: + Core::System& system; + Service* const service; + + std::vector<std::unique_ptr<Worker>> workers; +}; + +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 8d4952c0e..a74be9370 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -2,18 +2,138 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include <fmt/format.h> + +#include "common/microprofile.h" +#include "common/thread.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/thread.h" #include "core/hle/service/sockets/bsd.h" +#include "core/hle/service/sockets/sockets_translate.h" +#include "core/network/network.h" +#include "core/network/sockets.h" namespace Service::Sockets { +namespace { + +bool IsConnectionBased(Type type) { + switch (type) { + case Type::STREAM: + return true; + case Type::DGRAM: + return false; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); + return false; + } +} + +} // Anonymous namespace + +void BSD::PollWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout); +} + +void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) { + ctx.WriteBuffer(write_buffer); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); +} + +void BSD::AcceptWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer); +} + +void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) { + ctx.WriteBuffer(write_buffer); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); + rb.Push<u32>(static_cast<u32>(write_buffer.size())); +} + +void BSD::ConnectWork::Execute(BSD* bsd) { + bsd_errno = bsd->ConnectImpl(fd, addr); +} + +void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); + rb.PushEnum(bsd_errno); +} + +void BSD::RecvWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message); +} + +void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) { + ctx.WriteBuffer(message); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); +} + +void BSD::RecvFromWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr); +} + +void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) { + ctx.WriteBuffer(message, 0); + if (!addr.empty()) { + ctx.WriteBuffer(addr, 1); + } + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); + rb.Push<u32>(static_cast<u32>(addr.size())); +} + +void BSD::SendWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message); +} + +void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); +} + +void BSD::SendToWork::Execute(BSD* bsd) { + std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr); +} + +void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); +} + void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // bsd errno + rb.Push<s32>(0); // bsd errno } void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { @@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { void BSD::Socket(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; + const u32 domain = rp.Pop<u32>(); + const u32 type = rp.Pop<u32>(); + const u32 protocol = rp.Pop<u32>(); - u32 domain = rp.Pop<u32>(); - u32 type = rp.Pop<u32>(); - u32 protocol = rp.Pop<u32>(); - - LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol); + LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol); - u32 fd = next_fd++; + const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type), + static_cast<Protocol>(protocol)); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(fd); - rb.Push<u32>(0); // bsd errno + rb.Push<s32>(fd); + rb.PushEnum(bsd_errno); } void BSD::Select(Kernel::HLERequestContext& ctx) { @@ -52,67 +171,664 @@ void BSD::Select(Kernel::HLERequestContext& ctx) { rb.Push<u32>(0); // bsd errno } +void BSD::Poll(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 nfds = rp.Pop<s32>(); + const s32 timeout = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); + + ExecuteWork(ctx, "BSD:Poll", timeout != 0, + PollWork{ + .nfds = nfds, + .timeout = timeout, + .read_buffer = ctx.ReadBuffer(), + .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), + }); +} + +void BSD::Accept(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. fd={}", fd); + + ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd), + AcceptWork{ + .fd = fd, + .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), + }); +} + void BSD::Bind(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); - IPC::ResponseBuilder rb{ctx, 4}; + LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer())); } void BSD::Connect(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); - IPC::ResponseBuilder rb{ctx, 4}; + LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); + + ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd), + ConnectWork{ + .fd = fd, + .addr = ctx.ReadBuffer(), + }); +} + +void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. fd={}", fd); + std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); + const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer); + + ctx.WriteBuffer(write_buffer); + + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); + rb.PushEnum(bsd_errno); + rb.Push<u32>(static_cast<u32>(write_buffer.size())); +} + +void BSD::GetSockName(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. fd={}", fd); + + std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); + const Errno bsd_errno = GetSockNameImpl(fd, write_buffer); + + ctx.WriteBuffer(write_buffer); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); + rb.PushEnum(bsd_errno); + rb.Push<u32>(static_cast<u32>(write_buffer.size())); } void BSD::Listen(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + const s32 backlog = rp.Pop<s32>(); - IPC::ResponseBuilder rb{ctx, 4}; + LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog); + + BuildErrnoResponse(ctx, ListenImpl(fd, backlog)); +} + +void BSD::Fcntl(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + const s32 cmd = rp.Pop<s32>(); + const s32 arg = rp.Pop<s32>(); + LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg); + + const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg); + + IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + rb.Push<s32>(ret); + rb.PushEnum(bsd_errno); } void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; - IPC::ResponseBuilder rb{ctx, 4}; + const s32 fd = rp.Pop<s32>(); + const u32 level = rp.Pop<u32>(); + const OptName optname = static_cast<OptName>(rp.Pop<u32>()); - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + const std::vector<u8> buffer = ctx.ReadBuffer(); + const u8* optval = buffer.empty() ? nullptr : buffer.data(); + size_t optlen = buffer.size(); + + std::array<u64, 2> values; + if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) { + std::memcpy(values.data(), buffer.data(), sizeof(values)); + optlen = sizeof(values); + optval = reinterpret_cast<const u8*>(values.data()); + } + + LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, + static_cast<u32>(optname), optlen); + + BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); +} + +void BSD::Shutdown(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s32 fd = rp.Pop<s32>(); + const s32 how = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. fd={} how={}", fd, how); + + BuildErrnoResponse(ctx, ShutdownImpl(fd, how)); +} + +void BSD::Recv(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s32 fd = rp.Pop<s32>(); + const u32 flags = rp.Pop<u32>(); + + LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); + + ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd), + RecvWork{ + .fd = fd, + .flags = flags, + .message = std::vector<u8>(ctx.GetWriteBufferSize()), + }); +} + +void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s32 fd = rp.Pop<s32>(); + const u32 flags = rp.Pop<u32>(); + + LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags, + ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); + + ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd), + RecvFromWork{ + .fd = fd, + .flags = flags, + .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), + .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), + }); +} + +void BSD::Send(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const s32 fd = rp.Pop<s32>(); + const u32 flags = rp.Pop<u32>(); + + LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); + + ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd), + SendWork{ + .fd = fd, + .flags = flags, + .message = ctx.ReadBuffer(), + }); } void BSD::SendTo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + const u32 flags = rp.Pop<u32>(); + + LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags, + ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); + + ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd), + SendToWork{ + .fd = fd, + .flags = flags, + .message = ctx.ReadBuffer(0), + .addr = ctx.ReadBuffer(1), + }); +} - IPC::ResponseBuilder rb{ctx, 4}; +void BSD::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); - rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); + + ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd), + SendWork{ + .fd = fd, + .flags = 0, + .message = ctx.ReadBuffer(), + }); } void BSD::Close(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + + LOG_DEBUG(Service, "called. fd={}", fd); + + BuildErrnoResponse(ctx, CloseImpl(fd)); +} + +template <typename Work> +void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, + bool is_blocking, Work work) { + if (!is_blocking) { + work.Execute(this); + work.Response(ctx); + return; + } + + // Signal a dummy response to make IPC validation happy + // This will be overwritten by the SleepClientThread callback + work.Response(ctx); + + auto worker = worker_pool.CaptureWorker(); + + ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(), + worker->Callback<Work>(), worker->KernelEvent()); + + worker->SendWork(std::move(work)); +} + +std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { + if (type == Type::SEQPACKET) { + UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management"); + } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) { + UNIMPLEMENTED_MSG("SOCK_RAW errno management"); + } + + [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0; + UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type"); + type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000); + + const s32 fd = FindFreeFileDescriptorHandle(); + if (fd < 0) { + LOG_ERROR(Service, "No more file descriptors available"); + return {-1, Errno::MFILE}; + } + + FileDescriptor& descriptor = file_descriptors[fd].emplace(); + // ENONMEM might be thrown here + + LOG_INFO(Service, "New socket fd={}", fd); + + descriptor.socket = std::make_unique<Network::Socket>(); + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); + descriptor.is_connection_based = IsConnectionBased(type); + + return {fd, Errno::SUCCESS}; +} +std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, + s32 nfds, s32 timeout) { + if (write_buffer.size() < nfds * sizeof(PollFD)) { + return {-1, Errno::INVAL}; + } + + if (nfds == 0) { + // When no entries are provided, -1 is returned with errno zero + return {-1, Errno::SUCCESS}; + } + + const size_t length = std::min(read_buffer.size(), write_buffer.size()); + std::vector<PollFD> fds(nfds); + std::memcpy(fds.data(), read_buffer.data(), length); + + if (timeout >= 0) { + const s64 seconds = timeout / 1000; + const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000); + + if (seconds < 0) { + return {-1, Errno::INVAL}; + } + if (nanoseconds > 999'999'999) { + return {-1, Errno::INVAL}; + } + } else if (timeout != -1) { + return {-1, Errno::INVAL}; + } + + for (PollFD& pollfd : fds) { + ASSERT(pollfd.revents == 0); + + if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) { + LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd); + pollfd.revents = 0; + return {0, Errno::SUCCESS}; + } + + const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd]; + if (!descriptor) { + LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd); + pollfd.revents = POLL_NVAL; + return {0, Errno::SUCCESS}; + } + } + + std::vector<Network::PollFD> host_pollfds(fds.size()); + std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) { + Network::PollFD result; + result.socket = file_descriptors[pollfd.fd]->socket.get(); + result.events = TranslatePollEventsToHost(pollfd.events); + result.revents = 0; + return result; + }); + + const auto result = Network::Poll(host_pollfds, timeout); + + const size_t num = host_pollfds.size(); + for (size_t i = 0; i < num; ++i) { + fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents); + } + std::memcpy(write_buffer.data(), fds.data(), length); + + return Translate(result); +} + +std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + + const s32 new_fd = FindFreeFileDescriptorHandle(); + if (new_fd < 0) { + LOG_ERROR(Service, "No more file descriptors available"); + return {-1, Errno::MFILE}; + } + + FileDescriptor& descriptor = *file_descriptors[fd]; + auto [result, bsd_errno] = descriptor.socket->Accept(); + if (bsd_errno != Network::Errno::SUCCESS) { + return {-1, Translate(bsd_errno)}; + } + + FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace(); + new_descriptor.socket = std::move(result.socket); + new_descriptor.is_connection_based = descriptor.is_connection_based; + + ASSERT(write_buffer.size() == sizeof(SockAddrIn)); + const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); + std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); + + return {new_fd, Errno::SUCCESS}; +} + +Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + ASSERT(addr.size() == sizeof(SockAddrIn)); + SockAddrIn addr_in; + std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + + return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); +} + +Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); + SockAddrIn addr_in; + std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); + + return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); +} + +Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName(); + if (bsd_errno != Network::Errno::SUCCESS) { + return Translate(bsd_errno); + } + const SockAddrIn guest_addrin = Translate(addr_in); + + ASSERT(write_buffer.size() == sizeof(guest_addrin)); + std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + return Translate(bsd_errno); +} + +Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName(); + if (bsd_errno != Network::Errno::SUCCESS) { + return Translate(bsd_errno); + } + const SockAddrIn guest_addrin = Translate(addr_in); + + ASSERT(write_buffer.size() == sizeof(guest_addrin)); + std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); + return Translate(bsd_errno); +} + +Errno BSD::ListenImpl(s32 fd, s32 backlog) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + return Translate(file_descriptors[fd]->socket->Listen(backlog)); +} + +std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + + FileDescriptor& descriptor = *file_descriptors[fd]; + + switch (cmd) { + case FcntlCmd::GETFL: + ASSERT(arg == 0); + return {descriptor.flags, Errno::SUCCESS}; + case FcntlCmd::SETFL: { + const bool enable = (arg & FLAG_O_NONBLOCK) != 0; + const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); + if (bsd_errno != Errno::SUCCESS) { + return {-1, bsd_errno}; + } + descriptor.flags = arg; + return {0, Errno::SUCCESS}; + } + default: + UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd)); + return {-1, Errno::SUCCESS}; + } +} + +Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { + UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET + + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + Network::Socket* const socket = file_descriptors[fd]->socket.get(); + + if (optname == OptName::LINGER) { + ASSERT(optlen == sizeof(Linger)); + Linger linger; + std::memcpy(&linger, optval, sizeof(linger)); + ASSERT(linger.onoff == 0 || linger.onoff == 1); + + return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); + } + + ASSERT(optlen == sizeof(u32)); + u32 value; + std::memcpy(&value, optval, sizeof(value)); + + switch (optname) { + case OptName::REUSEADDR: + ASSERT(value == 0 || value == 1); + return Translate(socket->SetReuseAddr(value != 0)); + case OptName::BROADCAST: + ASSERT(value == 0 || value == 1); + return Translate(socket->SetBroadcast(value != 0)); + case OptName::SNDBUF: + return Translate(socket->SetSndBuf(value)); + case OptName::RCVBUF: + return Translate(socket->SetRcvBuf(value)); + case OptName::SNDTIMEO: + return Translate(socket->SetSndTimeo(value)); + case OptName::RCVTIMEO: + return Translate(socket->SetRcvTimeo(value)); + default: + UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname)); + return Errno::SUCCESS; + } +} + +Errno BSD::ShutdownImpl(s32 fd, s32 how) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how)); + return Translate(file_descriptors[fd]->socket->Shutdown(host_how)); +} + +std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + return Translate(file_descriptors[fd]->socket->Recv(flags, message)); +} + +std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, + std::vector<u8>& addr) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + + FileDescriptor& descriptor = *file_descriptors[fd]; + + Network::SockAddrIn addr_in{}; + Network::SockAddrIn* p_addr_in = nullptr; + if (descriptor.is_connection_based) { + // Connection based file descriptors (e.g. TCP) zero addr + addr.clear(); + } else { + p_addr_in = &addr_in; + } + + // Apply flags + if ((flags & FLAG_MSG_DONTWAIT) != 0) { + flags &= ~FLAG_MSG_DONTWAIT; + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(true); + } + } + + const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in)); + + // Restore original state + if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { + descriptor.socket->SetNonBlock(false); + } + + if (p_addr_in) { + if (ret < 0) { + addr.clear(); + } else { + ASSERT(addr.size() == sizeof(SockAddrIn)); + const SockAddrIn result = Translate(addr_in); + std::memcpy(addr.data(), &result, sizeof(result)); + } + } + + return {ret, bsd_errno}; +} + +std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + return Translate(file_descriptors[fd]->socket->Send(message, flags)); +} + +std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, + const std::vector<u8>& addr) { + if (!IsFileDescriptorValid(fd)) { + return {-1, Errno::BADF}; + } + + Network::SockAddrIn addr_in; + Network::SockAddrIn* p_addr_in = nullptr; + if (!addr.empty()) { + ASSERT(addr.size() == sizeof(SockAddrIn)); + SockAddrIn guest_addr_in; + std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); + addr_in = Translate(guest_addr_in); + p_addr_in = &addr_in; + } + + return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in)); +} + +Errno BSD::CloseImpl(s32 fd) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close()); + if (bsd_errno != Errno::SUCCESS) { + return bsd_errno; + } + + LOG_INFO(Service, "Close socket fd={}", fd); + + file_descriptors[fd].reset(); + return bsd_errno; +} + +s32 BSD::FindFreeFileDescriptorHandle() noexcept { + for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { + if (!file_descriptors[fd]) { + return fd; + } + } + return -1; +} + +bool BSD::IsFileDescriptorValid(s32 fd) const noexcept { + if (fd > static_cast<s32>(MAX_FD) || fd < 0) { + LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); + return false; + } + if (!file_descriptors[fd]) { + LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd); + return false; + } + return true; +} + +bool BSD::IsBlockingSocket(s32 fd) const noexcept { + // Inform invalid sockets as non-blocking + // This way we avoid using a worker thread as it will fail without blocking host + if (fd > static_cast<s32>(MAX_FD) || fd < 0) { + return false; + } + if (!file_descriptors[fd]) { + return false; + } + return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0; +} + +void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0); // ret - rb.Push<u32>(0); // bsd errno + rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); + rb.PushEnum(bsd_errno); } -BSD::BSD(const char* name) : ServiceFramework(name) { +BSD::BSD(Core::System& system, const char* name) + : ServiceFramework(name), worker_pool{system, this} { // clang-format off static const FunctionInfo functions[] = { {0, &BSD::RegisterClient, "RegisterClient"}, @@ -121,25 +837,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) { {3, nullptr, "SocketExempt"}, {4, nullptr, "Open"}, {5, &BSD::Select, "Select"}, - {6, nullptr, "Poll"}, + {6, &BSD::Poll, "Poll"}, {7, nullptr, "Sysctl"}, - {8, nullptr, "Recv"}, - {9, nullptr, "RecvFrom"}, - {10, nullptr, "Send"}, + {8, &BSD::Recv, "Recv"}, + {9, &BSD::RecvFrom, "RecvFrom"}, + {10, &BSD::Send, "Send"}, {11, &BSD::SendTo, "SendTo"}, - {12, nullptr, "Accept"}, + {12, &BSD::Accept, "Accept"}, {13, &BSD::Bind, "Bind"}, {14, &BSD::Connect, "Connect"}, - {15, nullptr, "GetPeerName"}, - {16, nullptr, "GetSockName"}, + {15, &BSD::GetPeerName, "GetPeerName"}, + {16, &BSD::GetSockName, "GetSockName"}, {17, nullptr, "GetSockOpt"}, {18, &BSD::Listen, "Listen"}, {19, nullptr, "Ioctl"}, - {20, nullptr, "Fcntl"}, + {20, &BSD::Fcntl, "Fcntl"}, {21, &BSD::SetSockOpt, "SetSockOpt"}, - {22, nullptr, "Shutdown"}, + {22, &BSD::Shutdown, "Shutdown"}, {23, nullptr, "ShutdownAllSockets"}, - {24, nullptr, "Write"}, + {24, &BSD::Write, "Write"}, {25, nullptr, "Read"}, {26, &BSD::Close, "Close"}, {27, nullptr, "DuplicateSocket"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 3098e3baf..357531951 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -4,30 +4,174 @@ #pragma once +#include <memory> +#include <string_view> +#include <vector> + +#include "common/common_types.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/service.h" +#include "core/hle/service/sockets/blocking_worker.h" +#include "core/hle/service/sockets/sockets.h" + +namespace Core { +class System; +} + +namespace Network { +class Socket; +} namespace Service::Sockets { class BSD final : public ServiceFramework<BSD> { public: - explicit BSD(const char* name); + explicit BSD(Core::System& system, const char* name); ~BSD() override; private: + /// Maximum number of file descriptors + static constexpr size_t MAX_FD = 128; + + struct FileDescriptor { + std::unique_ptr<Network::Socket> socket; + s32 flags = 0; + bool is_connection_based = false; + }; + + struct PollWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 nfds; + s32 timeout; + std::vector<u8> read_buffer; + std::vector<u8> write_buffer; + s32 ret{}; + Errno bsd_errno{}; + }; + + struct AcceptWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + std::vector<u8> write_buffer; + s32 ret{}; + Errno bsd_errno{}; + }; + + struct ConnectWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + std::vector<u8> addr; + Errno bsd_errno{}; + }; + + struct RecvWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + u32 flags; + std::vector<u8> message; + s32 ret{}; + Errno bsd_errno{}; + }; + + struct RecvFromWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + u32 flags; + std::vector<u8> message; + std::vector<u8> addr; + s32 ret{}; + Errno bsd_errno{}; + }; + + struct SendWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + u32 flags; + std::vector<u8> message; + s32 ret{}; + Errno bsd_errno{}; + }; + + struct SendToWork { + void Execute(BSD* bsd); + void Response(Kernel::HLERequestContext& ctx); + + s32 fd; + u32 flags; + std::vector<u8> message; + std::vector<u8> addr; + s32 ret{}; + Errno bsd_errno{}; + }; + void RegisterClient(Kernel::HLERequestContext& ctx); void StartMonitoring(Kernel::HLERequestContext& ctx); void Socket(Kernel::HLERequestContext& ctx); void Select(Kernel::HLERequestContext& ctx); + void Poll(Kernel::HLERequestContext& ctx); + void Accept(Kernel::HLERequestContext& ctx); void Bind(Kernel::HLERequestContext& ctx); void Connect(Kernel::HLERequestContext& ctx); + void GetPeerName(Kernel::HLERequestContext& ctx); + void GetSockName(Kernel::HLERequestContext& ctx); void Listen(Kernel::HLERequestContext& ctx); + void Fcntl(Kernel::HLERequestContext& ctx); void SetSockOpt(Kernel::HLERequestContext& ctx); + void Shutdown(Kernel::HLERequestContext& ctx); + void Recv(Kernel::HLERequestContext& ctx); + void RecvFrom(Kernel::HLERequestContext& ctx); + void Send(Kernel::HLERequestContext& ctx); void SendTo(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); - /// Id to use for the next open file descriptor. - u32 next_fd = 1; + template <typename Work> + void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, + bool is_blocking, Work work); + + std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); + std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, + s32 nfds, s32 timeout); + std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer); + Errno BindImpl(s32 fd, const std::vector<u8>& addr); + Errno ConnectImpl(s32 fd, const std::vector<u8>& addr); + Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer); + Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); + Errno ListenImpl(s32 fd, s32 backlog); + std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); + Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); + Errno ShutdownImpl(s32 fd, s32 how); + std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); + std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, + std::vector<u8>& addr); + std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message); + std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, + const std::vector<u8>& addr); + Errno CloseImpl(s32 fd); + + s32 FindFreeFileDescriptorHandle() noexcept; + bool IsFileDescriptorValid(s32 fd) const noexcept; + bool IsBlockingSocket(s32 fd) const noexcept; + + void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; + + std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; + + BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork, + SendToWork> + worker_pool; }; class BSDCFG final : public ServiceFramework<BSDCFG> { diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp index 08d2d306a..1d27f7906 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp @@ -10,9 +10,9 @@ namespace Service::Sockets { -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); - std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager); + std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager); std::make_shared<BSDCFG>()->InstallAsService(service_manager); std::make_shared<ETHC_C>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index ca8a6a7e0..89a410076 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -4,11 +4,94 @@ #pragma once +#include "common/common_types.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::Sockets { +enum class Errno : u32 { + SUCCESS = 0, + BADF = 9, + AGAIN = 11, + INVAL = 22, + MFILE = 24, + NOTCONN = 107, +}; + +enum class Domain : u32 { + INET = 2, +}; + +enum class Type : u32 { + STREAM = 1, + DGRAM = 2, + RAW = 3, + SEQPACKET = 5, +}; + +enum class Protocol : u32 { + UNSPECIFIED = 0, + ICMP = 1, + TCP = 6, + UDP = 17, +}; + +enum class OptName : u32 { + REUSEADDR = 0x4, + BROADCAST = 0x20, + LINGER = 0x80, + SNDBUF = 0x1001, + RCVBUF = 0x1002, + SNDTIMEO = 0x1005, + RCVTIMEO = 0x1006, +}; + +enum class ShutdownHow : s32 { + RD = 0, + WR = 1, + RDWR = 2, +}; + +enum class FcntlCmd : s32 { + GETFL = 3, + SETFL = 4, +}; + +struct SockAddrIn { + u8 len; + u8 family; + u16 portno; + std::array<u8, 4> ip; + std::array<u8, 8> zeroes; +}; + +struct PollFD { + s32 fd; + u16 events; + u16 revents; +}; + +struct Linger { + u32 onoff; + u32 linger; +}; + +constexpr u16 POLL_IN = 0x01; +constexpr u16 POLL_PRI = 0x02; +constexpr u16 POLL_OUT = 0x04; +constexpr u16 POLL_ERR = 0x08; +constexpr u16 POLL_HUP = 0x10; +constexpr u16 POLL_NVAL = 0x20; + +constexpr u32 FLAG_MSG_DONTWAIT = 0x80; + +constexpr u32 FLAG_O_NONBLOCK = 0x800; + /// Registers all Sockets services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp new file mode 100644 index 000000000..2e626fd86 --- /dev/null +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -0,0 +1,165 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <utility> + +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/sockets/sockets_translate.h" +#include "core/network/network.h" + +namespace Service::Sockets { + +Errno Translate(Network::Errno value) { + switch (value) { + case Network::Errno::SUCCESS: + return Errno::SUCCESS; + case Network::Errno::BADF: + return Errno::BADF; + case Network::Errno::AGAIN: + return Errno::AGAIN; + case Network::Errno::INVAL: + return Errno::INVAL; + case Network::Errno::MFILE: + return Errno::MFILE; + case Network::Errno::NOTCONN: + return Errno::NOTCONN; + default: + UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value)); + return Errno::SUCCESS; + } +} + +std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) { + return {value.first, Translate(value.second)}; +} + +Network::Domain Translate(Domain domain) { + switch (domain) { + case Domain::INET: + return Network::Domain::INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); + return {}; + } +} + +Domain Translate(Network::Domain domain) { + switch (domain) { + case Network::Domain::INET: + return Domain::INET; + default: + UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); + return {}; + } +} + +Network::Type Translate(Type type) { + switch (type) { + case Type::STREAM: + return Network::Type::STREAM; + case Type::DGRAM: + return Network::Type::DGRAM; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); + } +} + +Network::Protocol Translate(Type type, Protocol protocol) { + switch (protocol) { + case Protocol::UNSPECIFIED: + LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); + switch (type) { + case Type::DGRAM: + return Network::Protocol::UDP; + case Type::STREAM: + return Network::Protocol::TCP; + default: + return Network::Protocol::TCP; + } + case Protocol::TCP: + return Network::Protocol::TCP; + case Protocol::UDP: + return Network::Protocol::UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol)); + return Network::Protocol::TCP; + } +} + +u16 TranslatePollEventsToHost(u32 flags) { + u32 result = 0; + const auto translate = [&result, &flags](u32 from, u32 to) { + if ((flags & from) != 0) { + flags &= ~from; + result |= to; + } + }; + translate(POLL_IN, Network::POLL_IN); + translate(POLL_PRI, Network::POLL_PRI); + translate(POLL_OUT, Network::POLL_OUT); + translate(POLL_ERR, Network::POLL_ERR); + translate(POLL_HUP, Network::POLL_HUP); + translate(POLL_NVAL, Network::POLL_NVAL); + + UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags); + return static_cast<u16>(result); +} + +u16 TranslatePollEventsToGuest(u32 flags) { + u32 result = 0; + const auto translate = [&result, &flags](u32 from, u32 to) { + if ((flags & from) != 0) { + flags &= ~from; + result |= to; + } + }; + + translate(Network::POLL_IN, POLL_IN); + translate(Network::POLL_PRI, POLL_PRI); + translate(Network::POLL_OUT, POLL_OUT); + translate(Network::POLL_ERR, POLL_ERR); + translate(Network::POLL_HUP, POLL_HUP); + translate(Network::POLL_NVAL, POLL_NVAL); + + UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags); + return static_cast<u16>(result); +} + +Network::SockAddrIn Translate(SockAddrIn value) { + ASSERT(value.len == 0 || value.len == sizeof(value)); + + return { + .family = Translate(static_cast<Domain>(value.family)), + .ip = value.ip, + .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), + }; +} + +SockAddrIn Translate(Network::SockAddrIn value) { + return { + .len = sizeof(SockAddrIn), + .family = static_cast<u8>(Translate(value.family)), + .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), + .ip = value.ip, + .zeroes = {}, + }; +} + +Network::ShutdownHow Translate(ShutdownHow how) { + switch (how) { + case ShutdownHow::RD: + return Network::ShutdownHow::RD; + case ShutdownHow::WR: + return Network::ShutdownHow::WR; + case ShutdownHow::RDWR: + return Network::ShutdownHow::RDWR; + default: + UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how)); + return {}; + } +} + +} // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h new file mode 100644 index 000000000..e498913d4 --- /dev/null +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -0,0 +1,48 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <utility> + +#include "common/common_types.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/network/network.h" + +namespace Service::Sockets { + +/// Translate abstract errno to guest errno +Errno Translate(Network::Errno value); + +/// Translate abstract return value errno pair to guest return value errno pair +std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); + +/// Translate guest domain to abstract domain +Network::Domain Translate(Domain domain); + +/// Translate abstract domain to guest domain +Domain Translate(Network::Domain domain); + +/// Translate guest type to abstract type +Network::Type Translate(Type type); + +/// Translate guest protocol to abstract protocol +Network::Protocol Translate(Type type, Protocol protocol); + +/// Translate abstract poll event flags to guest poll event flags +u16 TranslatePollEventsToHost(u32 flags); + +/// Translate guest poll event flags to abstract poll event flags +u16 TranslatePollEventsToGuest(u32 flags); + +/// Translate guest socket address structure to abstract socket address structure +Network::SockAddrIn Translate(SockAddrIn value); + +/// Translate abstract socket address structure to guest socket address structure +SockAddrIn Translate(Network::SockAddrIn value); + +/// Translate guest shutdown mode to abstract shutdown mode +Network::ShutdownHow Translate(ShutdownHow how); + +} // namespace Service::Sockets diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 69152d0ac..bdf0439f2 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -820,7 +820,10 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend const ResultCode result{ ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)}; calendar.time.year = static_cast<s16>(calendar_time.year); - calendar.time.month = calendar_time.month + 1; // Internal impl. uses 0-indexed month + + // Internal impl. uses 0-indexed month + calendar.time.month = static_cast<s8>(calendar_time.month + 1); + calendar.time.day = calendar_time.day; calendar.time.hour = calendar_time.hour; calendar.time.minute = calendar_time.minute; @@ -872,13 +875,15 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time, s64& posix_time) const { posix_time = 0; - CalendarTimeInternal internal_time{}; - internal_time.year = calendar_time.year; - internal_time.month = calendar_time.month - 1; // Internal impl. uses 0-indexed month - internal_time.day = calendar_time.day; - internal_time.hour = calendar_time.hour; - internal_time.minute = calendar_time.minute; - internal_time.second = calendar_time.second; + CalendarTimeInternal internal_time{ + .year = calendar_time.year, + // Internal impl. uses 0-indexed month + .month = static_cast<s8>(calendar_time.month - 1), + .day = calendar_time.day, + .hour = calendar_time.hour, + .minute = calendar_time.minute, + .second = calendar_time.second, + }; s32 hour{internal_time.hour}; s32 minute{internal_time.minute}; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 480d34725..5b0e371fe 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -159,7 +159,7 @@ public: header.data_size = static_cast<u32_le>(write_index - sizeof(Header)); header.data_offset = sizeof(Header); header.objects_size = 4; - header.objects_offset = sizeof(Header) + header.data_size; + header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size); std::memcpy(buffer.data(), &header, sizeof(Header)); return buffer; @@ -215,10 +215,9 @@ public: explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPConnectRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } @@ -279,10 +278,9 @@ public: : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPSetPreallocatedBufferRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); data = Read<Data>(); buffer = Read<NVFlinger::IGBPBuffer>(); } @@ -306,15 +304,40 @@ protected: } }; +class IGBPCancelBufferRequestParcel : public Parcel { +public: + explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { + Deserialize(); + } + + void DeserializeData() override { + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); + data = Read<Data>(); + } + + struct Data { + u32_le slot; + Service::Nvidia::MultiFence multi_fence; + }; + + Data data; +}; + +class IGBPCancelBufferResponseParcel : public Parcel { +protected: + void SerializeData() override { + Write<u32>(0); // Success + } +}; + class IGBPDequeueBufferRequestParcel : public Parcel { public: explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPDequeueBufferRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } @@ -333,7 +356,6 @@ class IGBPDequeueBufferResponseParcel : public Parcel { public: explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence) : slot(slot), multi_fence(multi_fence) {} - ~IGBPDequeueBufferResponseParcel() override = default; protected: void SerializeData() override { @@ -352,10 +374,9 @@ public: explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPRequestBufferRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); slot = Read<u32_le>(); } @@ -384,10 +405,9 @@ public: explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPQueueBufferRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); data = Read<Data>(); } @@ -447,10 +467,9 @@ public: explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) { Deserialize(); } - ~IGBPQueryRequestParcel() override = default; void DeserializeData() override { - std::u16string token = ReadInterfaceToken(); + [[maybe_unused]] const std::u16string token = ReadInterfaceToken(); type = Read<u32_le>(); } @@ -596,7 +615,12 @@ private: break; } case TransactionId::CancelBuffer: { - LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer"); + IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()}; + + buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence); + + IGBPCancelBufferResponseParcel response{}; + ctx.WriteBuffer(response.Serialize()); break; } case TransactionId::Disconnect: { 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 } AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( - Kernel::Process& process) { + Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -141,9 +141,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect continue; } - const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; - const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( - process, *module_file, code_size, should_pass_arguments, false)}; + const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, code_size, should_pass_arguments, false); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } @@ -168,9 +168,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect } const VAddr load_addr{next_load_addr}; - const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; - const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( - process, *module_file, load_addr, should_pass_arguments, true, pm)}; + const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; + const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( + process, system, *module_file, load_addr, should_pass_arguments, true, pm); if (!tentative_next_load_addr) { return {ResultStatus::ErrorLoadingNSO, {}}; } @@ -192,8 +192,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect // Register the RomFS if a ".romfs" file was found if (romfs_iter != files.end() && *romfs_iter != nullptr) { romfs = *romfs_iter; - Core::System::GetInstance().GetFileSystemController().RegisterRomFS( - std::make_unique<FileSys::RomFSFactory>(*this)); + system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( + *this, system.GetContentProvider(), system.GetFileSystemController())); } 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 @@ #include "core/file_sys/program_metadata.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace Loader { /** @@ -37,7 +41,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 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) { return FileType::Error; } -AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { +AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process, + [[maybe_unused]] Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } 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 @@ #include "common/common_types.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace Loader { /// Loads an ELF/AXF file @@ -26,7 +30,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; }; } // namespace Loader diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 40fa03ad1..2a905d3e4 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -16,7 +16,7 @@ namespace Loader { namespace { constexpr u32 PageAlignSize(u32 size) { - return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; + return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); } } // Anonymous namespace @@ -43,7 +43,8 @@ FileType AppLoader_KIP::GetFileType() const { : FileType::Error; } -AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { +AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process, + [[maybe_unused]] Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } 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 @@ #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace FileSys { class KIP; } @@ -26,7 +30,7 @@ public: FileType GetFileType() const override; - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; private: 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 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/vfs.h" +namespace Core { +class System; +} + namespace FileSys { class NACP; } // namespace FileSys @@ -154,9 +158,10 @@ public: /** * Load the application and return the created Process instance * @param process The newly created process. + * @param system The system that this process is being loaded under. * @return The status result of the operation. */ - virtual LoadResult Load(Kernel::Process& process) = 0; + virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0; /** * 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 { return IdentifyTypeImpl(*nax); } -AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { +AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process, + [[maybe_unused]] Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -65,7 +66,7 @@ AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { return {nca_status, {}}; } - const auto result = nca_loader->Load(process); + const auto result = nca_loader->Load(process, system); if (result.first != ResultStatus::Success) { return result; } 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 @@ #include "common/common_types.h" #include "core/loader/loader.h" -namespace FileSys { +namespace Core { +class System; +} +namespace FileSys { class NAX; - } // namespace FileSys namespace Loader { @@ -33,7 +35,7 @@ public: FileType GetFileType() const override; - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; 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) { return FileType::Error; } -AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { +AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -52,14 +52,14 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); - const auto load_result = directory_loader->Load(process); + const auto load_result = directory_loader->Load(process, system); if (load_result.first != ResultStatus::Success) { return load_result; } if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { - Core::System::GetInstance().GetFileSystemController().RegisterRomFS( - std::make_unique<FileSys::RomFSFactory>(*this)); + system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( + *this, system.GetContentProvider(), system.GetFileSystemController())); } 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 @@ #include "core/file_sys/vfs.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace FileSys { class NCA; } @@ -33,7 +37,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; u64 ReadRomFSIVFCOffset() const override; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 906544bc9..5f4b3104b 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -127,7 +127,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { } static constexpr u32 PageAlignSize(u32 size) { - return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; + return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); } static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, @@ -208,7 +208,7 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi return LoadNroImpl(process, file.ReadAllBytes(), file.GetName()); } -AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { +AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -218,8 +218,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { } if (romfs != nullptr) { - Core::System::GetInstance().GetFileSystemController().RegisterRomFS( - std::make_unique<FileSys::RomFSFactory>(*this)); + system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( + *this, system.GetContentProvider(), system.GetFileSystemController())); } 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 @@ #include "common/common_types.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace FileSys { class NACP; } @@ -37,7 +41,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadProgramId(u64& out_program_id) override; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 575330a86..497f438a1 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -47,7 +47,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, } constexpr u32 PageAlignSize(u32 size) { - return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK; + return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK); } } // Anonymous namespace @@ -71,21 +71,21 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { return FileType::NSO; } -std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, +std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system, const FileSys::VfsFile& file, VAddr load_base, bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm) { if (file.GetSize() < sizeof(NSOHeader)) { - return {}; + return std::nullopt; } NSOHeader nso_header{}; if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { - return {}; + return std::nullopt; } if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { - return {}; + return std::nullopt; } // Build program image @@ -148,7 +148,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, // Apply cheats if they exist and the program has a valid title ID if (pm) { - auto& system = Core::System::GetInstance(); system.SetCurrentProcessBuildID(nso_header.build_id); const auto cheats = pm->CreateCheatList(system, nso_header.build_id); if (!cheats.empty()) { @@ -166,7 +165,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, return load_base + image_size; } -AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { +AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -175,7 +174,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { // Load module const VAddr base_address = process.PageTable().GetCodeRegionStart(); - if (!LoadModule(process, *file, base_address, true, true)) { + if (!LoadModule(process, system, *file, base_address, true, true)) { return {ResultStatus::ErrorLoadingNSO, {}}; } diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index b210830f0..d331096ae 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -12,6 +12,10 @@ #include "core/file_sys/patch_manager.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace Kernel { class Process; } @@ -55,7 +59,7 @@ struct NSOHeader { static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size."); static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable."); -constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; +constexpr u32 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; struct NSOArgumentHeader { u32_le allocated_size; @@ -80,12 +84,12 @@ public: return IdentifyType(file); } - static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, - VAddr load_base, bool should_pass_arguments, - bool load_into_process, + static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system, + const FileSys::VfsFile& file, VAddr load_base, + bool should_pass_arguments, bool load_into_process, std::optional<FileSys::PatchManager> pm = {}); - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadNSOModules(Modules& modules) override; 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) { return FileType::Error; } -AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { +AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -99,15 +99,14 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; } - const auto result = secondary_loader->Load(process); + const auto result = secondary_loader->Load(process, system); if (result.first != ResultStatus::Success) { return result; } FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( - std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); } 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 @@ #include "core/file_sys/vfs.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace FileSys { class NACP; class NSP; @@ -35,7 +39,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; 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) { return FileType::Error; } -AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { +AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) { if (is_loaded) { return {ResultStatus::ErrorAlreadyLoaded, {}}; } @@ -66,15 +66,14 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { return {ResultStatus::ErrorMissingProductionKeyFile, {}}; } - const auto result = nca_loader->Load(process); + const auto result = nca_loader->Load(process, system); if (result.first != ResultStatus::Success) { return result; } FileSys::VirtualFile update_raw; if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { - Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( - std::move(update_raw)); + system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); } 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 @@ #include "core/file_sys/vfs.h" #include "core/loader/loader.h" +namespace Core { +class System; +} + namespace FileSys { class NACP; class XCI; @@ -35,7 +39,7 @@ public: return IdentifyType(file); } - LoadResult Load(Kernel::Process& process) override; + LoadResult Load(Kernel::Process& process, Core::System& system) override; ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; u64 ReadRomFSIVFCOffset() const override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 86d17c6cb..b88aa5c40 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -120,9 +120,9 @@ struct Memory::Impl { if ((addr & 1) == 0) { return Read<u16_le>(addr); } else { - const u8 a{Read<u8>(addr)}; - const u8 b{Read<u8>(addr + sizeof(u8))}; - return (static_cast<u16>(b) << 8) | a; + const u32 a{Read<u8>(addr)}; + const u32 b{Read<u8>(addr + sizeof(u8))}; + return static_cast<u16>((b << 8) | a); } } @@ -130,9 +130,9 @@ struct Memory::Impl { if ((addr & 3) == 0) { return Read<u32_le>(addr); } else { - const u16 a{Read16(addr)}; - const u16 b{Read16(addr + sizeof(u16))}; - return (static_cast<u32>(b) << 16) | a; + const u32 a{Read16(addr)}; + const u32 b{Read16(addr + sizeof(u16))}; + return (b << 16) | a; } } @@ -567,7 +567,7 @@ struct Memory::Impl { * @param page_table The page table to use to perform the mapping. * @param base The base address to begin mapping at. * @param size The total size of the range in bytes. - * @param memory The memory to map. + * @param target The target address to begin mapping from. * @param type The page type to map the memory as. */ void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index e503118dd..2dd0eb0f8 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -19,10 +19,24 @@ #include "core/memory/cheat_engine.h" namespace Core::Memory { - +namespace { constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; +std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { + auto end_index = start_index; + while (data[end_index] != match) { + ++end_index; + if (end_index > data.size() || + (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) { + return {}; + } + } + + return data.substr(start_index, end_index - start_index); +} +} // Anonymous namespace + StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) : metadata(metadata), system(system) {} @@ -82,26 +96,9 @@ CheatParser::~CheatParser() = default; TextCheatParser::~TextCheatParser() = default; -namespace { -template <char match> -std::string_view ExtractName(std::string_view data, std::size_t start_index) { - auto end_index = start_index; - while (data[end_index] != match) { - ++end_index; - if (end_index > data.size() || - (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) { - return {}; - } - } - - return data.substr(start_index, end_index - start_index); -} -} // Anonymous namespace - -std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, - std::string_view data) const { +std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const { std::vector<CheatEntry> out(1); - std::optional<u64> current_entry = std::nullopt; + std::optional<u64> current_entry; for (std::size_t i = 0; i < data.size(); ++i) { if (::isspace(data[i])) { @@ -115,7 +112,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, return {}; } - const auto name = ExtractName<'}'>(data, i + 1); + const auto name = ExtractName(data, i + 1, '}'); if (name.empty()) { return {}; } @@ -132,7 +129,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, current_entry = out.size(); out.emplace_back(); - const auto name = ExtractName<']'>(data, i + 1); + const auto name = ExtractName(data, i + 1, ']'); if (name.empty()) { return {}; } @@ -156,8 +153,9 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, return {}; } + const auto value = static_cast<u32>(std::stoul(hex, nullptr, 0x10)); out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] = - std::stoul(hex, nullptr, 0x10); + value; i += 8; } else { 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 { public: virtual ~CheatParser(); - virtual std::vector<CheatEntry> Parse(const Core::System& system, - std::string_view data) const = 0; + [[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0; }; // CheatParser implementation that parses text files @@ -56,7 +55,7 @@ class TextCheatParser final : public CheatParser { public: ~TextCheatParser() override; - std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override; + [[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override; }; // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp index 56d173b5e..4b3bb4366 100644 --- a/src/core/network/network.cpp +++ b/src/core/network/network.cpp @@ -238,14 +238,14 @@ SockAddrIn TranslateToSockAddrIn(sockaddr input_) { return result; } -u16 TranslatePollEvents(u16 events) { - u16 result = 0; +u16 TranslatePollEvents(u32 events) { + u32 result = 0; - if (events & POLL_IN) { + if ((events & POLL_IN) != 0) { events &= ~POLL_IN; result |= POLLIN; } - if (events & POLL_PRI) { + if ((events & POLL_PRI) != 0) { events &= ~POLL_PRI; #ifdef _WIN32 LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); @@ -253,20 +253,20 @@ u16 TranslatePollEvents(u16 events) { result |= POLL_PRI; #endif } - if (events & POLL_OUT) { + if ((events & POLL_OUT) != 0) { events &= ~POLL_OUT; result |= POLLOUT; } UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events); - return result; + return static_cast<u16>(result); } -u16 TranslatePollRevents(u16 revents) { - u16 result = 0; - const auto translate = [&result, &revents](int host, unsigned guest) { - if (revents & host) { +u16 TranslatePollRevents(u32 revents) { + u32 result = 0; + const auto translate = [&result, &revents](u32 host, u32 guest) { + if ((revents & host) != 0) { revents &= ~host; result |= guest; } @@ -280,7 +280,7 @@ u16 TranslatePollRevents(u16 revents) { UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); - return result; + return static_cast<u16>(result); } template <typename T> @@ -350,7 +350,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { } for (size_t i = 0; i < num; ++i) { - pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); + pollfds[i].revents = TranslatePollRevents(static_cast<u32>(host_pollfds[i].revents)); } if (result > 0) { @@ -408,7 +408,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() { Errno Socket::Connect(SockAddrIn addr_in) { const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); - if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) { + if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) { return Errno::SUCCESS; } @@ -503,10 +503,10 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { ASSERT(flags == 0); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); - const int result = + const auto result = recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); if (result != SOCKET_ERROR) { - return {result, Errno::SUCCESS}; + return {static_cast<s32>(result), Errno::SUCCESS}; } switch (const int ec = LastError()) { @@ -531,14 +531,14 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock socklen_t* const p_addrlen = addr ? &addrlen : nullptr; sockaddr* const p_addr_in = addr ? &addr_in : nullptr; - const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()), - static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); + const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), + static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); if (result != SOCKET_ERROR) { if (addr) { ASSERT(addrlen == sizeof(addr_in)); *addr = TranslateToSockAddrIn(addr_in); } - return {result, Errno::SUCCESS}; + return {static_cast<s32>(result), Errno::SUCCESS}; } switch (const int ec = LastError()) { @@ -558,10 +558,10 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); ASSERT(flags == 0); - const int result = send(fd, reinterpret_cast<const char*>(message.data()), - static_cast<int>(message.size()), 0); + const auto result = send(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0); if (result != SOCKET_ERROR) { - return {result, Errno::SUCCESS}; + return {static_cast<s32>(result), Errno::SUCCESS}; } const int ec = LastError(); @@ -591,10 +591,10 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, to = &host_addr_in; } - const int result = sendto(fd, reinterpret_cast<const char*>(message.data()), - static_cast<int>(message.size()), 0, to, tolen); + const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()), + static_cast<int>(message.size()), 0, to, tolen); if (result != SOCKET_ERROR) { - return {result, Errno::SUCCESS}; + return {static_cast<s32>(result), Errno::SUCCESS}; } const int ec = LastError(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d328fb8b7..28d3f9099 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -13,56 +13,6 @@ namespace Settings { -namespace NativeButton { -const std::array<const char*, NumButtons> mapping = {{ - "button_a", - "button_b", - "button_x", - "button_y", - "button_lstick", - "button_rstick", - "button_l", - "button_r", - "button_zl", - "button_zr", - "button_plus", - "button_minus", - "button_dleft", - "button_dup", - "button_dright", - "button_ddown", - "button_lstick_left", - "button_lstick_up", - "button_lstick_right", - "button_lstick_down", - "button_rstick_left", - "button_rstick_up", - "button_rstick_right", - "button_rstick_down", - "button_sl", - "button_sr", - "button_home", - "button_screenshot", -}}; -} - -namespace NativeAnalog { -const std::array<const char*, NumAnalogs> mapping = {{ - "lstick", - "rstick", -}}; -} - -namespace NativeMouseButton { -const std::array<const char*, NumMouseButtons> mapping = {{ - "left", - "right", - "middle", - "forward", - "back", -}}; -} - Values values = {}; bool configuring_global = true; diff --git a/src/core/settings.h b/src/core/settings.h index bb145f193..9834f44bb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -12,340 +12,10 @@ #include <string> #include <vector> #include "common/common_types.h" +#include "input_common/settings.h" namespace Settings { -namespace NativeButton { -enum Values { - A, - B, - X, - Y, - LStick, - RStick, - L, - R, - ZL, - ZR, - Plus, - Minus, - - DLeft, - DUp, - DRight, - DDown, - - LStick_Left, - LStick_Up, - LStick_Right, - LStick_Down, - - RStick_Left, - RStick_Up, - RStick_Right, - RStick_Down, - - SL, - SR, - - Home, - Screenshot, - - NumButtons, -}; - -constexpr int BUTTON_HID_BEGIN = A; -constexpr int BUTTON_NS_BEGIN = Home; - -constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN; -constexpr int BUTTON_NS_END = NumButtons; - -constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; -constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; - -extern const std::array<const char*, NumButtons> mapping; - -} // namespace NativeButton - -namespace NativeAnalog { -enum Values { - LStick, - RStick, - - NumAnalogs, -}; - -constexpr int STICK_HID_BEGIN = LStick; -constexpr int STICK_HID_END = NumAnalogs; -constexpr int NUM_STICKS_HID = NumAnalogs; - -extern const std::array<const char*, NumAnalogs> mapping; -} // namespace NativeAnalog - -namespace NativeMouseButton { -enum Values { - Left, - Right, - Middle, - Forward, - Back, - - NumMouseButtons, -}; - -constexpr int MOUSE_HID_BEGIN = Left; -constexpr int MOUSE_HID_END = NumMouseButtons; -constexpr int NUM_MOUSE_HID = NumMouseButtons; - -extern const std::array<const char*, NumMouseButtons> mapping; -} // namespace NativeMouseButton - -namespace NativeKeyboard { -enum Keys { - None, - Error, - - A = 4, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - N1, - N2, - N3, - N4, - N5, - N6, - N7, - N8, - N9, - N0, - Enter, - Escape, - Backspace, - Tab, - Space, - Minus, - Equal, - LeftBrace, - RightBrace, - Backslash, - Tilde, - Semicolon, - Apostrophe, - Grave, - Comma, - Dot, - Slash, - CapsLockKey, - - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - - SystemRequest, - ScrollLockKey, - Pause, - Insert, - Home, - PageUp, - Delete, - End, - PageDown, - Right, - Left, - Down, - Up, - - NumLockKey, - KPSlash, - KPAsterisk, - KPMinus, - KPPlus, - KPEnter, - KP1, - KP2, - KP3, - KP4, - KP5, - KP6, - KP7, - KP8, - KP9, - KP0, - KPDot, - - Key102, - Compose, - Power, - KPEqual, - - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - - Open, - Help, - Properties, - Front, - Stop, - Repeat, - Undo, - Cut, - Copy, - Paste, - Find, - Mute, - VolumeUp, - VolumeDown, - CapsLockActive, - NumLockActive, - ScrollLockActive, - KPComma, - - KPLeftParenthesis, - KPRightParenthesis, - - LeftControlKey = 0xE0, - LeftShiftKey, - LeftAltKey, - LeftMetaKey, - RightControlKey, - RightShiftKey, - RightAltKey, - RightMetaKey, - - MediaPlayPause, - MediaStopCD, - MediaPrevious, - MediaNext, - MediaEject, - MediaVolumeUp, - MediaVolumeDown, - MediaMute, - MediaWebsite, - MediaBack, - MediaForward, - MediaStop, - MediaFind, - MediaScrollUp, - MediaScrollDown, - MediaEdit, - MediaSleep, - MediaCoffee, - MediaRefresh, - MediaCalculator, - - NumKeyboardKeys, -}; - -static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys."); - -enum Modifiers { - LeftControl, - LeftShift, - LeftAlt, - LeftMeta, - RightControl, - RightShift, - RightAlt, - RightMeta, - CapsLock, - ScrollLock, - NumLock, - - NumKeyboardMods, -}; - -constexpr int KEYBOARD_KEYS_HID_BEGIN = None; -constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys; -constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys; - -constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl; -constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods; -constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; - -} // namespace NativeKeyboard - -using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; -using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; -using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; -using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; -using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; - -constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; -constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; -constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; -constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; - -enum class ControllerType { - ProController, - DualJoycon, - RightJoycon, - LeftJoycon, -}; - -struct PlayerInput { - bool connected; - ControllerType type; - ButtonsRaw buttons; - AnalogsRaw analogs; - - u32 body_color_right; - u32 button_color_right; - u32 body_color_left; - u32 button_color_left; -}; - -struct TouchscreenInput { - bool enabled; - std::string device; - - u32 finger; - u32 diameter_x; - u32 diameter_y; - u32 rotation_angle; -}; - enum class RendererBackend { OpenGL = 0, Vulkan = 1, @@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 { enum class CPUAccuracy { Accurate = 0, - DebugMode = 1, + Unsafe = 1, + DebugMode = 2, }; extern bool configuring_global; @@ -396,6 +67,11 @@ private: Type local{}; }; +struct TouchFromButtonMap { + std::string name; + std::vector<std::string> buttons; +}; + struct Values { // Audio std::string audio_device_id; @@ -419,6 +95,9 @@ struct Values { bool cpuopt_misc_ir; bool cpuopt_reduce_misalign_checks; + bool cpuopt_unsafe_unfuse_fma; + bool cpuopt_unsafe_reduce_fp_error; + // Renderer Setting<RendererBackend> renderer_backend; bool renderer_debug; @@ -457,6 +136,8 @@ struct Values { // Controls std::array<PlayerInput, 10> players; + bool use_docked_mode; + bool mouse_enabled; std::string mouse_device; MouseButtonsRaw mouse_buttons; @@ -469,14 +150,19 @@ struct Values { ButtonsRaw debug_pad_buttons; AnalogsRaw debug_pad_analogs; + bool vibration_enabled; + + bool motion_enabled; std::string motion_device; + std::string touch_device; TouchscreenInput touchscreen; std::atomic_bool is_device_reload_pending{true}; + bool use_touch_from_button; + int touch_from_button_map_index; std::string udp_input_address; u16 udp_input_port; u8 udp_pad_index; - - bool use_docked_mode; + std::vector<TouchFromButtonMap> touch_from_button_maps; // Data Storage bool use_virtual_sd; |