diff options
Diffstat (limited to '')
113 files changed, 5721 insertions, 5389 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b2b0dbe05..5c99c00f5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -272,22 +272,22 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/applets/applet_controller.cpp + hle/service/am/applets/applet_controller.h + hle/service/am/applets/applet_error.cpp + hle/service/am/applets/applet_error.h + hle/service/am/applets/applet_general_backend.cpp + hle/service/am/applets/applet_general_backend.h + hle/service/am/applets/applet_profile_select.cpp + hle/service/am/applets/applet_profile_select.h + hle/service/am/applets/applet_software_keyboard.cpp + hle/service/am/applets/applet_software_keyboard.h + hle/service/am/applets/applet_software_keyboard_types.h + hle/service/am/applets/applet_web_browser.cpp + hle/service/am/applets/applet_web_browser.h + hle/service/am/applets/applet_web_browser_types.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 - hle/service/am/applets/general_backend.h - hle/service/am/applets/profile_select.cpp - hle/service/am/applets/profile_select.h - hle/service/am/applets/software_keyboard.cpp - hle/service/am/applets/software_keyboard.h - hle/service/am/applets/software_keyboard_types.h - hle/service/am/applets/web_browser.cpp - hle/service/am/applets/web_browser.h - hle/service/am/applets/web_types.h hle/service/am/idle.cpp hle/service/am/idle.h hle/service/am/omm.cpp @@ -300,10 +300,10 @@ add_library(core STATIC hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp hle/service/apm/apm.h - hle/service/apm/controller.cpp - hle/service/apm/controller.h - hle/service/apm/interface.cpp - hle/service/apm/interface.h + hle/service/apm/apm_controller.cpp + hle/service/apm/apm_controller.h + hle/service/apm/apm_interface.cpp + hle/service/apm/apm_interface.h hle/service/audio/audctl.cpp hle/service/audio/audctl.h hle/service/audio/auddbg.cpp @@ -335,8 +335,8 @@ add_library(core STATIC hle/service/bcat/backend/backend.h hle/service/bcat/bcat.cpp hle/service/bcat/bcat.h - hle/service/bcat/module.cpp - hle/service/bcat/module.h + hle/service/bcat/bcat_module.cpp + hle/service/bcat/bcat_module.h hle/service/bpc/bpc.cpp hle/service/bpc/bpc.h hle/service/btdrv/btdrv.cpp @@ -382,8 +382,8 @@ add_library(core STATIC hle/service/friend/errors.h hle/service/friend/friend.cpp hle/service/friend/friend.h - hle/service/friend/interface.cpp - hle/service/friend/interface.h + hle/service/friend/friend_interface.cpp + hle/service/friend/friend_interface.h hle/service/glue/arp.cpp hle/service/glue/arp.h hle/service/glue/bgtc.cpp @@ -393,8 +393,8 @@ add_library(core STATIC hle/service/glue/errors.h hle/service/glue/glue.cpp hle/service/glue/glue.h - hle/service/glue/manager.cpp - hle/service/glue/manager.h + hle/service/glue/glue_manager.cpp + hle/service/glue/glue_manager.h hle/service/grc/grc.cpp hle/service/grc/grc.h hle/service/hid/hid.cpp @@ -435,10 +435,10 @@ add_library(core STATIC hle/service/lm/lm.h hle/service/mig/mig.cpp hle/service/mig/mig.h - hle/service/mii/manager.cpp - hle/service/mii/manager.h hle/service/mii/mii.cpp hle/service/mii/mii.h + hle/service/mii/mii_manager.cpp + hle/service/mii/mii_manager.h hle/service/mii/raw_data.cpp hle/service/mii/raw_data.h hle/service/mii/types.h @@ -486,11 +486,11 @@ add_library(core STATIC hle/service/nvdrv/devices/nvhost_vic.h hle/service/nvdrv/devices/nvmap.cpp hle/service/nvdrv/devices/nvmap.h - hle/service/nvdrv/interface.cpp - hle/service/nvdrv/interface.h hle/service/nvdrv/nvdata.h hle/service/nvdrv/nvdrv.cpp hle/service/nvdrv/nvdrv.h + hle/service/nvdrv/nvdrv_interface.cpp + hle/service/nvdrv/nvdrv_interface.h hle/service/nvdrv/nvmemp.cpp hle/service/nvdrv/nvmemp.h hle/service/nvdrv/syncpoint_manager.cpp @@ -503,10 +503,10 @@ add_library(core STATIC hle/service/olsc/olsc.h hle/service/pcie/pcie.cpp hle/service/pcie/pcie.h - hle/service/pctl/module.cpp - hle/service/pctl/module.h hle/service/pctl/pctl.cpp hle/service/pctl/pctl.h + hle/service/pctl/pctl_module.cpp + hle/service/pctl/pctl_module.h hle/service/pcv/pcv.cpp hle/service/pcv/pcv.h hle/service/pm/pm.cpp @@ -517,6 +517,8 @@ add_library(core STATIC hle/service/psc/psc.h hle/service/ptm/psm.cpp hle/service/ptm/psm.h + hle/service/kernel_helpers.cpp + hle/service/kernel_helpers.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp @@ -529,10 +531,10 @@ add_library(core STATIC hle/service/set/set_sys.h hle/service/set/settings.cpp hle/service/set/settings.h - hle/service/sm/controller.cpp - hle/service/sm/controller.h hle/service/sm/sm.cpp hle/service/sm/sm.h + hle/service/sm/sm_controller.cpp + hle/service/sm/sm_controller.h hle/service/sockets/bsd.cpp hle/service/sockets/bsd.h hle/service/sockets/ethc.cpp @@ -547,10 +549,10 @@ add_library(core STATIC hle/service/sockets/sockets_translate.h hle/service/spl/csrng.cpp hle/service/spl/csrng.h - hle/service/spl/module.cpp - hle/service/spl/module.h hle/service/spl/spl.cpp hle/service/spl/spl.h + hle/service/spl/spl_module.cpp + hle/service/spl/spl_module.h hle/service/spl/spl_results.h hle/service/spl/spl_types.h hle/service/ssl/ssl.cpp @@ -559,8 +561,6 @@ add_library(core STATIC hle/service/time/ephemeral_network_system_clock_context_writer.h hle/service/time/ephemeral_network_system_clock_core.h hle/service/time/errors.h - hle/service/time/interface.cpp - hle/service/time/interface.h hle/service/time/local_system_clock_context_writer.h hle/service/time/network_system_clock_context_writer.h hle/service/time/standard_local_system_clock_core.h @@ -578,6 +578,8 @@ add_library(core STATIC hle/service/time/tick_based_steady_clock_core.h hle/service/time/time.cpp hle/service/time/time.h + hle/service/time/time_interface.cpp + hle/service/time/time_interface.h hle/service/time/time_manager.cpp hle/service/time/time_manager.h hle/service/time/time_sharedmemory.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 77a44f862..b0d89c539 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -150,7 +150,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* config.far_code_offset = 400_MiB; // Safe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { + if (Settings::values.cpu_debug_mode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -183,20 +183,28 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { + if (Settings::values.cpuopt_unsafe_unfuse_fma) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; } - if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr.GetValue()) { + if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; } - if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; } } + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + } + 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 75332e348..bf27ffe71 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -190,7 +190,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* config.far_code_offset = 400_MiB; // Safe optimizations - if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::DebugMode) { + if (Settings::values.cpu_debug_mode) { if (!Settings::values.cpuopt_page_tables) { config.page_table = nullptr; } @@ -223,20 +223,28 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* // Unsafe optimizations if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { config.unsafe_optimizations = true; - if (Settings::values.cpuopt_unsafe_unfuse_fma.GetValue()) { + if (Settings::values.cpuopt_unsafe_unfuse_fma) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; } - if (Settings::values.cpuopt_unsafe_reduce_fp_error.GetValue()) { + if (Settings::values.cpuopt_unsafe_reduce_fp_error) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; } - if (Settings::values.cpuopt_unsafe_inaccurate_nan.GetValue()) { + if (Settings::values.cpuopt_unsafe_inaccurate_nan) { config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; } - if (Settings::values.cpuopt_unsafe_fastmem_check.GetValue()) { + if (Settings::values.cpuopt_unsafe_fastmem_check) { config.fastmem_address_space_bits = 64; } } + // Curated optimizations + if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { + config.unsafe_optimizations = true; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; + config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; + config.fastmem_address_space_bits = 64; + } + return std::make_shared<Dynarmic::A64::Jit>(config); } diff --git a/src/core/core.cpp b/src/core/core.cpp index 891f1cb49..15226cf41 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -35,9 +35,9 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/apm_controller.h" #include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" @@ -216,9 +216,9 @@ struct System::Impl { } ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, - std::size_t program_index) { + u64 program_id, std::size_t program_index) { app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), - program_index); + program_id, program_index); if (!app_loader) { LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); @@ -269,11 +269,10 @@ struct System::Impl { } } - u64 title_id{0}; - if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { + if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) { LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result); } - perf_stats = std::make_unique<PerfStats>(title_id); + perf_stats = std::make_unique<PerfStats>(program_id); // Reset counters and set time origin to current frame GetAndResetPerfStats(); perf_stats->BeginSystemFrame(); @@ -459,8 +458,8 @@ void System::Shutdown() { } System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, - std::size_t program_index) { - return impl->Load(*this, emu_window, filepath, program_index); + u64 program_id, std::size_t program_index) { + return impl->Load(*this, emu_window, filepath, program_id, program_index); } bool System::IsPoweredOn() const { diff --git a/src/core/core.h b/src/core/core.h index 8b93ba998..b93c32e60 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -175,7 +175,7 @@ public: * @returns ResultStatus code, indicating if the operation succeeded. */ [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath, - std::size_t program_index = 0); + u64 program_id = 0, std::size_t program_index = 0); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index db2f6a955..755d3303e 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -29,7 +29,7 @@ constexpr std::array partition_names{ "logo", }; -XCI::XCI(VirtualFile file_, std::size_t program_index) +XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index) : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, partitions(partition_names.size()), partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { @@ -63,12 +63,12 @@ XCI::XCI(VirtualFile file_, std::size_t program_index) secure_partition = std::make_shared<NSP>( main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]), - program_index); + program_id, program_index); ncas = secure_partition->GetNCAsCollapsed(); program = secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); - program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); + program_nca_status = secure_partition->GetProgramStatus(); if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) { program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; } @@ -174,6 +174,10 @@ u64 XCI::GetProgramTitleID() const { return secure_partition->GetProgramTitleID(); } +std::vector<u64> XCI::GetProgramTitleIDs() const { + return secure_partition->GetProgramTitleIDs(); +} + u32 XCI::GetSystemUpdateVersion() { const auto update = GetPartition(XCIPartition::Update); if (update == nullptr) { @@ -229,9 +233,11 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { } std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { - const auto iter = - std::find_if(ncas.begin(), ncas.end(), - [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); + const auto program_id = secure_partition->GetProgramTitleID(); + const auto iter = std::find_if( + ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) { + return nca->GetType() == type && nca->GetTitleId() == program_id; + }); return iter == ncas.end() ? nullptr : *iter; } diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 4960e90fe..0fd9fa87c 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; class XCI : public ReadOnlyVfsDirectory { public: - explicit XCI(VirtualFile file, std::size_t program_index = 0); + explicit XCI(VirtualFile file, u64 program_id = 0, size_t program_index = 0); ~XCI() override; Loader::ResultStatus GetStatus() const; @@ -104,6 +104,7 @@ public: VirtualFile GetLogoPartitionRaw() const; u64 GetProgramTitleID() const; + std::vector<u64> GetProgramTitleIDs() const; u32 GetSystemUpdateVersion(); u64 GetSystemUpdateTitleID() const; diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 24eff210f..7019a7a68 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -5,7 +5,6 @@ #include <algorithm> #include <cstring> #include <optional> -#include <ranges> #include <utility> #include "common/logging/log.h" diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index d51d469e3..f192dffa5 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -20,8 +20,9 @@ namespace FileSys { -NSP::NSP(VirtualFile file_, std::size_t program_index_) - : file(std::move(file_)), program_index(program_index_), status{Loader::ResultStatus::Success}, +NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_) + : file(std::move(file_)), expected_program_id(title_id_), + program_index(program_index_), status{Loader::ResultStatus::Success}, pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { if (pfs->GetStatus() != Loader::ResultStatus::Success) { status = pfs->GetStatus(); @@ -46,60 +47,59 @@ Loader::ResultStatus NSP::GetStatus() const { return status; } -Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { +Loader::ResultStatus NSP::GetProgramStatus() const { if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { return Loader::ResultStatus::Success; } - const auto iter = program_status.find(title_id); + const auto iter = program_status.find(GetProgramTitleID()); if (iter == program_status.end()) return Loader::ResultStatus::ErrorNSPMissingProgramNCA; return iter->second; } -u64 NSP::GetFirstTitleID() const { - if (IsExtractedType()) { - return GetProgramTitleID(); - } - - if (program_status.empty()) - return 0; - return program_status.begin()->first; -} - u64 NSP::GetProgramTitleID() const { if (IsExtractedType()) { - if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { - return 0; - } + return GetExtractedTitleID() + program_index; + } - ProgramMetadata meta; - if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { - return meta.GetTitleID(); - } else { - return 0; + auto program_id = expected_program_id; + if (program_id == 0) { + if (!program_status.empty()) { + program_id = program_status.begin()->first; } } - const auto out = GetFirstTitleID(); - if ((out & 0x800) == 0) - return out; + program_id = program_id + program_index; + if (program_status.find(program_id) != program_status.end()) { + return program_id; + } - const auto ids = GetTitleIDs(); + const auto ids = GetProgramTitleIDs(); const auto iter = std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); - return iter == ids.end() ? out : *iter; + return iter == ids.end() ? 0 : *iter; +} + +u64 NSP::GetExtractedTitleID() const { + if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { + return 0; + } + + ProgramMetadata meta; + if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { + return meta.GetTitleID(); + } else { + return 0; + } } -std::vector<u64> NSP::GetTitleIDs() const { +std::vector<u64> NSP::GetProgramTitleIDs() const { if (IsExtractedType()) { - return {GetProgramTitleID()}; + return {GetExtractedTitleID()}; } - std::vector<u64> out; - out.reserve(ncas.size()); - for (const auto& kv : ncas) - out.push_back(kv.first); + std::vector<u64> out{program_ids.cbegin(), program_ids.cend()}; return out; } @@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType if (extracted) LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); - const auto title_id_iter = ncas.find(title_id + program_index); + const auto title_id_iter = ncas.find(title_id); if (title_id_iter == ncas.end()) return nullptr; @@ -160,7 +160,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { if (extracted) LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); - const auto nca = GetNCA(title_id, type); + const auto nca = GetNCA(title_id, type, title_type); if (nca != nullptr) return nca->GetBaseFile(); return nullptr; @@ -286,6 +286,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { if (next_nca->GetType() == NCAContentType::Program) { program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); + program_ids.insert(next_nca->GetTitleId() & 0xFFFFFFFFFFFFF000); } if (next_nca->GetStatus() != Loader::ResultStatus::Success && diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index ecb3b6f15..030f36c09 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -6,6 +6,7 @@ #include <map> #include <memory> +#include <set> #include <vector> #include "common/common_types.h" #include "core/file_sys/vfs.h" @@ -27,15 +28,15 @@ enum class ContentRecordType : u8; class NSP : public ReadOnlyVfsDirectory { public: - explicit NSP(VirtualFile file_, std::size_t program_index_ = 0); + explicit NSP(VirtualFile file_, u64 title_id = 0, std::size_t program_index_ = 0); ~NSP() override; Loader::ResultStatus GetStatus() const; - Loader::ResultStatus GetProgramStatus(u64 title_id) const; + Loader::ResultStatus GetProgramStatus() const; // Should only be used when one title id can be assured. - u64 GetFirstTitleID() const; u64 GetProgramTitleID() const; - std::vector<u64> GetTitleIDs() const; + u64 GetExtractedTitleID() const; + std::vector<u64> GetProgramTitleIDs() const; bool IsExtractedType() const; @@ -69,6 +70,7 @@ private: VirtualFile file; + const u64 expected_program_id; const std::size_t program_index; bool extracted = false; @@ -78,6 +80,7 @@ private: std::shared_ptr<PartitionFilesystem> pfs; // Map title id -> {map type -> NCA} std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; + std::set<u64> program_ids; std::vector<VirtualFile> ticket_files; Core::Crypto::KeyManager& keys; diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h index 506eb35bb..228a548d4 100644 --- a/src/core/frontend/applets/software_keyboard.h +++ b/src/core/frontend/applets/software_keyboard.h @@ -9,7 +9,7 @@ #include "common/common_types.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" namespace Core::Frontend { diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h index d7bd44c27..915dde677 100644 --- a/src/core/frontend/applets/web_browser.h +++ b/src/core/frontend/applets/web_browser.h @@ -7,7 +7,7 @@ #include <functional> #include <string_view> -#include "core/hle/service/am/applets/web_types.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" namespace Core::Frontend { diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index 645f26e91..290db505e 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp @@ -5,7 +5,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hardware_interrupt_manager.h" -#include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/sm/sm.h" namespace Core::Hardware { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 28ed6265a..ca68fc325 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -58,6 +58,9 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co void SessionRequestHandler::ClientConnected(KServerSession* session) { session->ClientConnected(shared_from_this()); + + // Ensure our server session is tracked globally. + kernel.RegisterServerSession(session); } void SessionRequestHandler::ClientDisconnected(KServerSession* session) { diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp index dbe237f09..c99a9ebb7 100644 --- a/src/core/hle/kernel/k_auto_object.cpp +++ b/src/core/hle/kernel/k_auto_object.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/kernel.h" namespace Kernel { @@ -11,4 +12,12 @@ KAutoObject* KAutoObject::Create(KAutoObject* obj) { return obj; } +void KAutoObject::RegisterWithKernel() { + kernel.RegisterKernelObject(this); +} + +void KAutoObject::UnregisterWithKernel() { + kernel.UnregisterKernelObject(this); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 88a052f65..e4fcdbc67 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -85,8 +85,12 @@ private: KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject); public: - explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {} - virtual ~KAutoObject() = default; + explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { + RegisterWithKernel(); + } + virtual ~KAutoObject() { + UnregisterWithKernel(); + } static KAutoObject* Create(KAutoObject* ptr); @@ -166,6 +170,10 @@ public: } } +private: + void RegisterWithKernel(); + void UnregisterWithKernel(); + protected: KernelCore& kernel; std::string name; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d1bd98051..8ead1a769 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -10,6 +10,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "common/logging/log.h" +#include "common/scope_exit.h" #include "common/settings.h" #include "core/core.h" #include "core/device_memory.h" @@ -43,6 +44,8 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); KThread* thread = KThread::Create(system.Kernel()); + SCOPE_EXIT({ thread->Close(); }); + ASSERT(KThread::InitializeUserThread(system, thread, entry_point, 0, stack_top, priority, owner_process.GetIdealCoreId(), &owner_process) .IsSuccess()); @@ -162,7 +165,7 @@ void KProcess::DecrementThreadCount() { ASSERT(num_threads > 0); if (const auto count = --num_threads; count == 0) { - UNIMPLEMENTED_MSG("Process termination is not implemented!"); + LOG_WARNING(Kernel, "Process termination is not fully implemented."); } } @@ -406,6 +409,9 @@ void KProcess::Finalize() { resource_limit->Close(); } + // Finalize the handle table and close any open handles. + handle_table.Finalize(); + // Perform inherited finalization. KAutoObjectWithSlabHeapAndContainer<KProcess, KSynchronizationObject>::Finalize(); } diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 5c3c13ce6..b9f24475c 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -28,7 +28,10 @@ namespace Kernel { KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} -KServerSession::~KServerSession() {} +KServerSession::~KServerSession() { + // Ensure that the global list tracking server sessions does not hold on to a reference. + kernel.UnregisterServerSession(this); +} void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, std::shared_ptr<SessionRequestManager> manager_) { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 64bd0c494..92fbc5532 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -61,6 +61,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); + global_handle_table->Initialize(KHandleTable::MaxTableSize); is_phantom_mode_for_singlecore = false; @@ -90,9 +91,39 @@ struct KernelCore::Impl { } void Shutdown() { + // Shutdown all processes. + if (current_process) { + current_process->Finalize(); + current_process->Close(); + current_process = nullptr; + } process_list.clear(); - // Ensures all service threads gracefully shutdown + // Close all open server ports. + std::unordered_set<KServerPort*> server_ports_; + { + std::lock_guard lk(server_ports_lock); + server_ports_ = server_ports; + server_ports.clear(); + } + for (auto* server_port : server_ports_) { + server_port->Close(); + } + // Close all open server sessions. + std::unordered_set<KServerSession*> server_sessions_; + { + std::lock_guard lk(server_sessions_lock); + server_sessions_ = server_sessions; + server_sessions.clear(); + } + for (auto* server_session : server_sessions_) { + server_session->Close(); + } + + // Ensure that the object list container is finalized and properly shutdown. + object_list_container.Finalize(); + + // Ensures all service threads gracefully shutdown. service_threads.clear(); next_object_id = 0; @@ -111,11 +142,7 @@ struct KernelCore::Impl { cores.clear(); - if (current_process) { - current_process->Close(); - current_process = nullptr; - } - + global_handle_table->Finalize(); global_handle_table.reset(); preemption_event = nullptr; @@ -142,6 +169,16 @@ struct KernelCore::Impl { // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others next_host_thread_id = Core::Hardware::NUM_CPU_CORES; + + // Track kernel objects that were not freed on shutdown + { + std::lock_guard lk(registered_objects_lock); + if (registered_objects.size()) { + LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", + registered_objects.size()); + registered_objects.clear(); + } + } } void InitializePhysicalCores() { @@ -630,6 +667,21 @@ struct KernelCore::Impl { user_slab_heap_size); } + KClientPort* CreateNamedServicePort(std::string name) { + auto search = service_interface_factory.find(name); + if (search == service_interface_factory.end()) { + UNIMPLEMENTED(); + return {}; + } + + KClientPort* port = &search->second(system.ServiceManager(), system); + { + std::lock_guard lk(server_ports_lock); + server_ports.insert(&port->GetParent()->GetServerPort()); + } + return port; + } + std::atomic<u32> next_object_id{0}; std::atomic<u64> next_kernel_process_id{KProcess::InitialKIPIDMin}; std::atomic<u64> next_user_process_id{KProcess::ProcessIDMin}; @@ -656,6 +708,12 @@ struct KernelCore::Impl { /// the ConnectToPort SVC. std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; NamedPortTable named_ports; + std::unordered_set<KServerPort*> server_ports; + std::unordered_set<KServerSession*> server_sessions; + std::unordered_set<KAutoObject*> registered_objects; + std::mutex server_ports_lock; + std::mutex server_sessions_lock; + std::mutex registered_objects_lock; std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; std::vector<Kernel::PhysicalCore> cores; @@ -844,12 +902,27 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory& } KClientPort* KernelCore::CreateNamedServicePort(std::string name) { - auto search = impl->service_interface_factory.find(name); - if (search == impl->service_interface_factory.end()) { - UNIMPLEMENTED(); - return {}; - } - return &search->second(impl->system.ServiceManager(), impl->system); + return impl->CreateNamedServicePort(std::move(name)); +} + +void KernelCore::RegisterServerSession(KServerSession* server_session) { + std::lock_guard lk(impl->server_sessions_lock); + impl->server_sessions.insert(server_session); +} + +void KernelCore::UnregisterServerSession(KServerSession* server_session) { + std::lock_guard lk(impl->server_sessions_lock); + impl->server_sessions.erase(server_session); +} + +void KernelCore::RegisterKernelObject(KAutoObject* object) { + std::lock_guard lk(impl->registered_objects_lock); + impl->registered_objects.insert(object); +} + +void KernelCore::UnregisterKernelObject(KAutoObject* object) { + std::lock_guard lk(impl->registered_objects_lock); + impl->registered_objects.erase(object); } bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2d01e1ae0..3a6db0b1c 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -45,6 +45,7 @@ class KPort; class KProcess; class KResourceLimit; class KScheduler; +class KServerSession; class KSession; class KSharedMemory; class KThread; @@ -185,6 +186,22 @@ public: /// Opens a port to a service previously registered with RegisterNamedService. KClientPort* CreateNamedServicePort(std::string name); + /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is + /// necessary because we do not emulate processes for HLE sessions. + void RegisterServerSession(KServerSession* server_session); + + /// Unregisters a server session previously registered with RegisterServerSession when it was + /// destroyed during the current emulation session. + void UnregisterServerSession(KServerSession* server_session); + + /// Registers all kernel objects with the global emulation state, this is purely for tracking + /// leaks after emulation has been shutdown. + void RegisterKernelObject(KAutoObject* object); + + /// Unregisters a kernel object previously registered with RegisterKernelObject when it was + /// destroyed during the current emulation session. + void UnregisterKernelObject(KAutoObject* object); + /// Determines whether or not the given port is a valid named port. bool IsValidNamedPort(NamedPortTable::const_iterator port) const; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8339e11a0..2eb532472 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -298,6 +298,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po // Create a session. KClientSession* session{}; R_TRY(port->CreateSession(std::addressof(session))); + port->Close(); // Register the session in the table, close the extra reference. handle_table.Register(*out, session); @@ -1439,11 +1440,6 @@ static void ExitProcess(Core::System& system) { LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, "Process has already exited"); - - current_process->PrepareForTermination(); - - // Kill the current thread - system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit(); } static void ExitProcess32(Core::System& system) { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index d1c1fb71d..2e969f2a8 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -26,7 +26,7 @@ #include "core/hle/service/acc/errors.h" #include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/glue/arp.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/sm/sm.h" #include "core/loader/loader.h" diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 0e3ad8ec6..a83a480cd 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -5,7 +5,7 @@ #pragma once #include "common/uuid.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/service.h" namespace Service::Account { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 23ebc1138..a538f82e3 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -24,16 +24,16 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.h" #include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/profile_select.h" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/am/idle.h" #include "core/hle/service/am/omm.h" #include "core/hle/service/am/spsm.h" #include "core/hle/service/am/tcap.h" -#include "core/hle/service/apm/controller.h" -#include "core/hle/service/apm/interface.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/apm/apm_interface.h" #include "core/hle/service/bcat/backend/backend.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp new file mode 100644 index 000000000..2721679c1 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_controller.cpp @@ -0,0 +1,272 @@ +// 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/applet_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::NpadStyleSet 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 = std::move(identification_colors), + .enable_explain_text = enable_text, + .explain_text = std::move(text), + .allow_pro_controller = npad_style_set.fullkey == 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_, LibraryAppletMode applet_mode_, + const Core::Frontend::ControllerApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +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); + + controller_applet_version = ControllerAppletVersion{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(), private_arg.size()); + ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), + "Unknown ControllerSupportArgPrivate revision={} with size={}", + controller_applet_version, controller_private_arg.arg_private_size); + + // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. + // Defer to arg_size to set the ControllerSupportMode. + if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { + switch (controller_private_arg.arg_size) { + case sizeof(ControllerSupportArgOld): + case sizeof(ControllerSupportArgNew): + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + case sizeof(ControllerUpdateFirmwareArg): + controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; + break; + case sizeof(ControllerKeyRemappingArg): + controller_private_arg.mode = + ControllerSupportMode::ShowControllerKeyRemappingForSystem; + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", + controller_private_arg.mode, controller_private_arg.arg_size); + controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; + break; + } + } + + // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. + // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. + if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { + if (controller_private_arg.flag_1 && + (controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate || + controller_private_arg.mode == + ControllerSupportMode::ShowControllerKeyRemappingForSystem)) { + controller_private_arg.caller = ControllerSupportCaller::System; + } else { + controller_private_arg.caller = ControllerSupportCaller::Application; + } + } + + switch (controller_private_arg.mode) { + case ControllerSupportMode::ShowControllerSupport: + case ControllerSupportMode::ShowControllerStrapGuide: { + const auto user_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(user_arg_storage != nullptr); + + const auto& user_arg = user_arg_storage->GetData(); + switch (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::Version5: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); + std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); + break; + case ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: + ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); + std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); + break; + default: + UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", + controller_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::ShowControllerFirmwareUpdate: { + const auto update_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(update_arg_storage != nullptr); + + const auto& update_arg = update_arg_storage->GetData(); + ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); + + std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); + break; + } + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: { + const auto remapping_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(remapping_arg_storage != nullptr); + + const auto& remapping_arg = remapping_arg_storage->GetData(); + ASSERT(remapping_arg.size() == sizeof(ControllerKeyRemappingArg)); + + std::memcpy(&controller_key_remapping_arg, remapping_arg.data(), remapping_arg.size()); + break; + } + 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 (controller_applet_version) { + case ControllerAppletVersion::Version3: + case ControllerAppletVersion::Version4: + case ControllerAppletVersion::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 ControllerAppletVersion::Version7: + case ControllerAppletVersion::Version8: + 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: + case ControllerSupportMode::ShowControllerKeyRemappingForSystem: + UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", + controller_private_arg.mode); + ConfigurationComplete(); + break; + default: { + ConfigurationComplete(); + break; + } + } +} + +void Controller::ConfigurationComplete() { + ControllerSupportResultInfo result_info{}; + + const auto& players = Settings::values.players.GetValue(); + + // 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, + [](const auto& player) { return player.connected; })); + + result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance( + players.begin(), std::find_if(players.begin(), players.end(), + [](const auto& 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>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h new file mode 100644 index 000000000..0a34c4fc0 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -0,0 +1,148 @@ +// 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 ControllerAppletVersion : 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 - 10.2.0 + Version8 = 0x8, // 11.0.0+ +}; + +enum class ControllerSupportMode : u8 { + ShowControllerSupport, + ShowControllerStrapGuide, + ShowControllerFirmwareUpdate, + ShowControllerKeyRemappingForSystem, + + MaxControllerSupportMode, +}; + +enum class ControllerSupportCaller : u8 { + Application, + System, + + MaxControllerSupportCaller, +}; + +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, 0x8 +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 ControllerUpdateFirmwareArg { + bool enable_force_update{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, + "ControllerUpdateFirmwareArg has incorrect size."); + +struct ControllerKeyRemappingArg { + u64 unknown{}; + u32 unknown_2{}; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(ControllerKeyRemappingArg) == 0x10, + "ControllerKeyRemappingArg 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_, LibraryAppletMode applet_mode_, + 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; + Core::System& system; + + ControllerAppletVersion controller_applet_version; + ControllerSupportArgPrivate controller_private_arg; + ControllerSupportArgOld controller_user_arg_old; + ControllerSupportArgNew controller_user_arg_new; + ControllerUpdateFirmwareArg controller_update_arg; + ControllerKeyRemappingArg controller_key_remapping_arg; + bool complete{false}; + ResultCode status{ResultSuccess}; + bool is_single_mode{false}; + std::vector<u8> out_data; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp new file mode 100644 index 000000000..ef6854d62 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -0,0 +1,194 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <cstring> +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/error.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_error.h" +#include "core/reporter.h" + +namespace Service::AM::Applets { + +#pragma pack(push, 4) +struct ShowError { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(4); + bool use_64bit_error_code; + INSERT_PADDING_BYTES_NOINIT(1); + u64 error_code_64; + u32 error_code_32; +}; +static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); +#pragma pack(pop) + +struct ShowErrorRecord { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + u64 posix_time; +}; +static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); + +struct SystemErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u64 error_code_64; + std::array<char, 8> language_code; + std::array<char, 0x800> main_text; + std::array<char, 0x800> detail_text; +}; +static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); + +struct ApplicationErrorArg { + u8 mode; + bool jump; + INSERT_PADDING_BYTES_NOINIT(6); + u32 error_code; + std::array<char, 8> language_code; + std::array<char, 0x800> main_text; + std::array<char, 0x800> detail_text; +}; +static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); + +union Error::ErrorArguments { + ShowError error; + ShowErrorRecord error_record; + SystemErrorArg system_error; + ApplicationErrorArg application_error; + std::array<u8, 0x1018> raw{}; +}; + +namespace { +template <typename T> +void CopyArgumentData(const std::vector<u8>& data, T& variable) { + ASSERT(data.size() >= sizeof(T)); + std::memcpy(&variable, data.data(), sizeof(T)); +} + +ResultCode Decode64BitError(u64 error) { + const auto description = (error >> 32) & 0x1FFF; + auto module = error & 0x3FF; + if (module >= 2000) + module -= 2000; + module &= 0x1FF; + return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; +} + +} // Anonymous namespace + +Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +Error::~Error() = default; + +void Error::Initialize() { + Applet::Initialize(); + args = std::make_unique<ErrorArguments>(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + + ASSERT(!data.empty()); + std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); + + switch (mode) { + case ErrorAppletMode::ShowError: + CopyArgumentData(data, args->error); + if (args->error.use_64bit_error_code) { + error_code = Decode64BitError(args->error.error_code_64); + } else { + error_code = ResultCode(args->error.error_code_32); + } + break; + case ErrorAppletMode::ShowSystemError: + CopyArgumentData(data, args->system_error); + error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); + break; + case ErrorAppletMode::ShowApplicationError: + CopyArgumentData(data, args->application_error); + error_code = ResultCode(args->application_error.error_code); + break; + case ErrorAppletMode::ShowErrorRecord: + CopyArgumentData(data, args->error_record); + error_code = Decode64BitError(args->error_record.error_code_64); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + } +} + +bool Error::TransactionComplete() const { + return complete; +} + +ResultCode Error::GetStatus() const { + return ResultSuccess; +} + +void Error::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data!"); +} + +void Error::Execute() { + if (complete) { + return; + } + + const auto callback = [this] { DisplayCompleted(); }; + const auto title_id = system.CurrentProcess()->GetTitleID(); + const auto& reporter{system.GetReporter()}; + + switch (mode) { + case ErrorAppletMode::ShowError: + reporter.SaveErrorReport(title_id, error_code); + frontend.ShowError(error_code, callback); + break; + case ErrorAppletMode::ShowSystemError: + case ErrorAppletMode::ShowApplicationError: { + const auto is_system = mode == ErrorAppletMode::ShowSystemError; + const auto& main_text = + is_system ? args->system_error.main_text : args->application_error.main_text; + const auto& detail_text = + is_system ? args->system_error.detail_text : args->application_error.detail_text; + + const auto main_text_string = + Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); + const auto detail_text_string = + Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); + + reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); + frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); + break; + } + case ErrorAppletMode::ShowErrorRecord: + reporter.SaveErrorReport(title_id, error_code, + fmt::format("{:016X}", args->error_record.posix_time)); + frontend.ShowErrorWithTimestamp( + error_code, std::chrono::seconds{args->error_record.posix_time}, callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + DisplayCompleted(); + } +} + +void Error::DisplayCompleted() { + complete = true; + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/applet_error.h index 8aa9046a5..8aa9046a5 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/applet_error.h diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp new file mode 100644 index 000000000..0f413f9a0 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -0,0 +1,255 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <string_view> + +#include "common/assert.h" +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/frontend/applets/general_frontend.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/reporter.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; + +static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { + std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); + for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received normal data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } + + storage = broker.PopInteractiveDataToApplet(); + for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { + const auto data = storage->GetData(); + LOG_INFO(Service_AM, + "called (STUBBED), during {} received interactive data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); + } +} + +Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +Auth::~Auth() = default; + +void Auth::Initialize() { + Applet::Initialize(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(data.size() >= 0xC); + + struct Arg { + INSERT_PADDING_BYTES(4); + AuthAppletType type; + u8 arg0; + u8 arg1; + u8 arg2; + INSERT_PADDING_BYTES(1); + }; + static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size."); + + Arg arg{}; + std::memcpy(&arg, data.data(), sizeof(Arg)); + + type = arg.type; + arg0 = arg.arg0; + arg1 = arg.arg1; + arg2 = arg.arg2; +} + +bool Auth::TransactionComplete() const { + return complete; +} + +ResultCode Auth::GetStatus() const { + return successful ? ResultSuccess : ERROR_INVALID_PIN; +} + +void Auth::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data."); +} + +void Auth::Execute() { + if (complete) { + return; + } + + const auto unimplemented_log = [this] { + UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, " + "arg1={:02X}, arg2={:02X}", + type, arg0, arg1, arg2); + }; + + switch (type) { + case AuthAppletType::ShowParentalAuthentication: { + const auto callback = [this](bool is_successful) { AuthFinished(is_successful); }; + + if (arg0 == 1 && arg1 == 0 && arg2 == 1) { + // ShowAuthenticatorForConfiguration + frontend.VerifyPINForSettings(callback); + } else if (arg1 == 0 && arg2 == 0) { + // ShowParentalAuthentication(bool) + frontend.VerifyPIN(callback, static_cast<bool>(arg0)); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::RegisterParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // RegisterParentalPasscode + frontend.RegisterPIN(callback); + } else { + unimplemented_log(); + } + break; + } + case AuthAppletType::ChangeParentalPasscode: { + const auto callback = [this] { AuthFinished(true); }; + + if (arg0 == 0 && arg1 == 0 && arg2 == 0) { + // ChangeParentalPasscode + frontend.ChangePIN(callback); + } else { + unimplemented_log(); + } + break; + } + default: + unimplemented_log(); + } +} + +void Auth::AuthFinished(bool is_successful) { + successful = is_successful; + + struct Return { + ResultCode result_code; + }; + static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); + + Return return_{GetStatus()}; + + std::vector<u8> out(sizeof(Return)); + std::memcpy(out.data(), &return_, sizeof(Return)); + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out))); + broker.SignalStateChanged(); +} + +PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +PhotoViewer::~PhotoViewer() = default; + +void PhotoViewer::Initialize() { + Applet::Initialize(); + complete = false; + + const auto storage = broker.PopNormalDataToApplet(); + ASSERT(storage != nullptr); + const auto data = storage->GetData(); + ASSERT(!data.empty()); + mode = static_cast<PhotoViewerAppletMode>(data[0]); +} + +bool PhotoViewer::TransactionComplete() const { + return complete; +} + +ResultCode PhotoViewer::GetStatus() const { + return ResultSuccess; +} + +void PhotoViewer::ExecuteInteractive() { + UNREACHABLE_MSG("Unexpected interactive applet data."); +} + +void PhotoViewer::Execute() { + if (complete) + return; + + const auto callback = [this] { ViewFinished(); }; + switch (mode) { + case PhotoViewerAppletMode::CurrentApp: + frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); + break; + case PhotoViewerAppletMode::AllApps: + frontend.ShowAllPhotos(callback); + break; + default: + UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); + } +} + +void PhotoViewer::ViewFinished() { + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); + broker.SignalStateChanged(); +} + +StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) + : Applet{system_, applet_mode_}, id{id_}, system{system_} {} + +StubApplet::~StubApplet() = default; + +void StubApplet::Initialize() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + Applet::Initialize(); + + const auto data = broker.PeekDataToAppletForDebug(); + system.GetReporter().SaveUnimplementedAppletReport( + static_cast<u32>(id), common_args.arguments_version, common_args.library_version, + common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, + data.normal, data.interactive); + + LogCurrentStorage(broker, "Initialize"); +} + +bool StubApplet::TransactionComplete() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return true; +} + +ResultCode StubApplet::GetStatus() const { + LOG_WARNING(Service_AM, "called (STUBBED)"); + return ResultSuccess; +} + +void StubApplet::ExecuteInteractive() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "ExecuteInteractive"); + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); + broker.PushInteractiveDataFromApplet( + std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); + broker.SignalStateChanged(); +} + +void StubApplet::Execute() { + LOG_WARNING(Service_AM, "called (STUBBED)"); + LogCurrentStorage(broker, "Execute"); + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); + broker.PushInteractiveDataFromApplet( + std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h index 7496ded88..7496ded88 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/applet_general_backend.h diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp new file mode 100644 index 000000000..bdc21778e --- /dev/null +++ b/src/core/hle/service/am/applets/applet_profile_select.cpp @@ -0,0 +1,78 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> + +#include "common/assert.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/profile_select.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_profile_select.h" + +namespace Service::AM::Applets { + +constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; + +ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +ProfileSelect::~ProfileSelect() = default; + +void ProfileSelect::Initialize() { + complete = false; + status = ResultSuccess; + final_data.clear(); + + Applet::Initialize(); + + const auto user_config_storage = broker.PopNormalDataToApplet(); + ASSERT(user_config_storage != nullptr); + const auto& user_config = user_config_storage->GetData(); + + ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); + std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); +} + +bool ProfileSelect::TransactionComplete() const { + return complete; +} + +ResultCode ProfileSelect::GetStatus() const { + return status; +} + +void ProfileSelect::ExecuteInteractive() { + UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); +} + +void ProfileSelect::Execute() { + if (complete) { + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); + return; + } + + frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); }); +} + +void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { + UserSelectionOutput output{}; + + if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { + output.result = 0; + output.uuid_selected = uuid->uuid; + } else { + status = ERR_USER_CANCELLED_SELECTION; + output.result = ERR_USER_CANCELLED_SELECTION.raw; + output.uuid_selected = Common::INVALID_UUID; + } + + final_data = std::vector<u8>(sizeof(UserSelectionOutput)); + std::memcpy(final_data.data(), &output, final_data.size()); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); + broker.SignalStateChanged(); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h index 8fb76e6c4..8fb76e6c4 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/applet_profile_select.h diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp new file mode 100644 index 000000000..7cae90609 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp @@ -0,0 +1,1082 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" +#include "core/core.h" +#include "core/frontend/applets/software_keyboard.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" + +namespace Service::AM::Applets { + +namespace { + +// The maximum number of UTF-16 characters that can be input into the swkbd text field. +constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; + +constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); +constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; +constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; + +constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { + switch (text_check_result) { + case SwkbdTextCheckResult::Success: + return "Success"; + case SwkbdTextCheckResult::Failure: + return "Failure"; + case SwkbdTextCheckResult::Confirm: + return "Confirm"; + case SwkbdTextCheckResult::Silent: + return "Silent"; + default: + UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); + return "Unknown"; + } +} + +void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply_type) { + std::memcpy(reply.data(), &state, sizeof(SwkbdState)); + std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); +} + +} // Anonymous namespace + +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} + +SoftwareKeyboard::~SoftwareKeyboard() = default; + +void SoftwareKeyboard::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", + applet_mode); + + LOG_DEBUG(Service_AM, + "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); + + swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; + + switch (applet_mode) { + case LibraryAppletMode::AllForeground: + InitializeForeground(); + break; + case LibraryAppletMode::Background: + case LibraryAppletMode::BackgroundIndirectDisplay: + InitializeBackground(applet_mode); + break; + default: + UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); + break; + } +} + +bool SoftwareKeyboard::TransactionComplete() const { + return complete; +} + +ResultCode SoftwareKeyboard::GetStatus() const { + return status; +} + +void SoftwareKeyboard::ExecuteInteractive() { + if (complete) { + return; + } + + if (is_background) { + ProcessInlineKeyboardRequest(); + } else { + ProcessTextCheck(); + } +} + +void SoftwareKeyboard::Execute() { + if (complete) { + return; + } + + if (is_background) { + return; + } + + ShowNormalKeyboard(); +} + +void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { + if (complete) { + return; + } + + if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { + SubmitForTextCheck(submitted_text); + } else { + SubmitNormalOutputAndExit(result, submitted_text); + } +} + +void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position) { + if (complete) { + return; + } + + current_text = std::move(submitted_text); + current_cursor_position = cursor_position; + + if (inline_use_utf8) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringUtf8; + break; + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorUtf8; + break; + case SwkbdReplyType::DecidedEnter: + reply_type = SwkbdReplyType::DecidedEnterUtf8; + break; + default: + break; + } + } + + if (use_changed_string_v2) { + switch (reply_type) { + case SwkbdReplyType::ChangedString: + reply_type = SwkbdReplyType::ChangedStringV2; + break; + case SwkbdReplyType::ChangedStringUtf8: + reply_type = SwkbdReplyType::ChangedStringUtf8V2; + break; + default: + break; + } + } + + if (use_moved_cursor_v2) { + switch (reply_type) { + case SwkbdReplyType::MovedCursor: + reply_type = SwkbdReplyType::MovedCursorV2; + break; + case SwkbdReplyType::MovedCursorUtf8: + reply_type = SwkbdReplyType::MovedCursorUtf8V2; + break; + default: + break; + } + } + + SendReply(reply_type); +} + +void SoftwareKeyboard::InitializeForeground() { + LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); + + is_background = false; + + const auto swkbd_config_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_config_storage != nullptr); + + const auto& swkbd_config_data = swkbd_config_storage->GetData(); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); + + std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); + + switch (swkbd_applet_version) { + case SwkbdAppletVersion::Version5: + case SwkbdAppletVersion::Version65542: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); + std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld)); + break; + case SwkbdAppletVersion::Version196615: + case SwkbdAppletVersion::Version262152: + case SwkbdAppletVersion::Version327689: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); + std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigOld2)); + break; + case SwkbdAppletVersion::Version393227: + case SwkbdAppletVersion::Version524301: + ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, + swkbd_config_data.size()); + ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); + std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), + sizeof(SwkbdConfigNew)); + break; + } + + const auto work_buffer_storage = broker.PopNormalDataToApplet(); + ASSERT(work_buffer_storage != nullptr); + + if (swkbd_config_common.initial_string_length == 0) { + InitializeFrontendKeyboard(); + return; + } + + const auto& work_buffer = work_buffer_storage->GetData(); + + std::vector<char16_t> initial_string(swkbd_config_common.initial_string_length); + + std::memcpy(initial_string.data(), + work_buffer.data() + swkbd_config_common.initial_string_offset, + swkbd_config_common.initial_string_length * sizeof(char16_t)); + + initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), + initial_string.size()); + + LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); + + InitializeFrontendKeyboard(); +} + +void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { + LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); + + is_background = true; + + const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(swkbd_inline_initialize_arg_storage != nullptr); + + const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); + ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); + + std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), + swkbd_inline_initialize_arg.size()); + + if (swkbd_initialize_arg.library_applet_mode_flag) { + ASSERT(library_applet_mode == LibraryAppletMode::Background); + } else { + ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); + } +} + +void SoftwareKeyboard::ProcessTextCheck() { + const auto text_check_storage = broker.PopInteractiveDataToApplet(); + ASSERT(text_check_storage != nullptr); + + const auto& text_check_data = text_check_storage->GetData(); + ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); + + SwkbdTextCheck swkbd_text_check; + + std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); + + std::u16string text_check_message = + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || + swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm + ? Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_text_check.text_check_message.data(), + swkbd_text_check.text_check_message.size()) + : u""; + + LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", + GetTextCheckResultName(swkbd_text_check.text_check_result), + Common::UTF16ToUTF8(text_check_message)); + + switch (swkbd_text_check.text_check_result) { + case SwkbdTextCheckResult::Success: + SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); + break; + case SwkbdTextCheckResult::Failure: + ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Confirm: + ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); + break; + case SwkbdTextCheckResult::Silent: + default: + break; + } +} + +void SoftwareKeyboard::ProcessInlineKeyboardRequest() { + const auto request_data_storage = broker.PopInteractiveDataToApplet(); + ASSERT(request_data_storage != nullptr); + + const auto& request_data = request_data_storage->GetData(); + ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); + + SwkbdRequestCommand request_command; + + std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); + + switch (request_command) { + case SwkbdRequestCommand::Finalize: + RequestFinalize(request_data); + break; + case SwkbdRequestCommand::SetUserWordInfo: + RequestSetUserWordInfo(request_data); + break; + case SwkbdRequestCommand::SetCustomizeDic: + RequestSetCustomizeDic(request_data); + break; + case SwkbdRequestCommand::Calc: + RequestCalc(request_data); + break; + case SwkbdRequestCommand::SetCustomizedDictionaries: + RequestSetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::UnsetCustomizedDictionaries: + RequestUnsetCustomizedDictionaries(request_data); + break; + case SwkbdRequestCommand::SetChangedStringV2Flag: + RequestSetChangedStringV2Flag(request_data); + break; + case SwkbdRequestCommand::SetMovedCursorV2Flag: + RequestSetMovedCursorV2Flag(request_data); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); + break; + } +} + +void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, + std::u16string submitted_text) { + std::vector<u8> out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); + + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, + utf8_submitted_text); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, + Common::UTF16ToUTF8(submitted_text)); + + std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); + std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), + submitted_text.size() * sizeof(char16_t)); + } + + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + + ExitKeyboard(); +} + +void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { + current_text = std::move(submitted_text); + + std::vector<u8> out_data(sizeof(u64) + STRING_BUFFER_SIZE); + + if (swkbd_config_common.use_utf8) { + std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); + const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, + utf8_submitted_text); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), + utf8_submitted_text.size()); + } else { + const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); + + LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, + Common::UTF16ToUTF8(current_text)); + + std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); + std::memcpy(out_data.data() + sizeof(u64), current_text.data(), + current_text.size() * sizeof(char16_t)); + } + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); +} + +void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { + switch (reply_type) { + case SwkbdReplyType::FinishedInitialize: + ReplyFinishedInitialize(); + break; + case SwkbdReplyType::Default: + ReplyDefault(); + break; + case SwkbdReplyType::ChangedString: + ReplyChangedString(); + break; + case SwkbdReplyType::MovedCursor: + ReplyMovedCursor(); + break; + case SwkbdReplyType::MovedTab: + ReplyMovedTab(); + break; + case SwkbdReplyType::DecidedEnter: + ReplyDecidedEnter(); + break; + case SwkbdReplyType::DecidedCancel: + ReplyDecidedCancel(); + break; + case SwkbdReplyType::ChangedStringUtf8: + ReplyChangedStringUtf8(); + break; + case SwkbdReplyType::MovedCursorUtf8: + ReplyMovedCursorUtf8(); + break; + case SwkbdReplyType::DecidedEnterUtf8: + ReplyDecidedEnterUtf8(); + break; + case SwkbdReplyType::UnsetCustomizeDic: + ReplyUnsetCustomizeDic(); + break; + case SwkbdReplyType::ReleasedUserWordInfo: + ReplyReleasedUserWordInfo(); + break; + case SwkbdReplyType::UnsetCustomizedDictionaries: + ReplyUnsetCustomizedDictionaries(); + break; + case SwkbdReplyType::ChangedStringV2: + ReplyChangedStringV2(); + break; + case SwkbdReplyType::MovedCursorV2: + ReplyMovedCursorV2(); + break; + case SwkbdReplyType::ChangedStringUtf8V2: + ReplyChangedStringUtf8V2(); + break; + case SwkbdReplyType::MovedCursorUtf8V2: + ReplyMovedCursorUtf8V2(); + break; + default: + UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); + ReplyDefault(); + break; + } +} + +void SoftwareKeyboard::ChangeState(SwkbdState state) { + swkbd_state = state; + + ReplyDefault(); +} + +void SoftwareKeyboard::InitializeFrontendKeyboard() { + if (is_background) { + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + appear_arg.ok_text.data(), appear_arg.ok_text.size()); + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + const s32 initial_cursor_position = + current_cursor_position > 0 ? current_cursor_position : 0; + + const auto text_draw_type = + max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{}, + .sub_text{}, + .guide_text{}, + .initial_text{current_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{appear_arg.type}, + .password_mode{SwkbdPasswordMode::Disabled}, + .text_draw_type{text_draw_type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .use_blur_background{false}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.InitializeKeyboard( + true, std::move(initialize_parameters), {}, + [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { + SubmitTextInline(reply_type, submitted_text, cursor_position); + }); + } else { + std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); + + std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); + + std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); + + std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); + + const u32 max_text_length = + swkbd_config_common.max_text_length > 0 && + swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? swkbd_config_common.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length + ? swkbd_config_common.min_text_length + : 0; + + const s32 initial_cursor_position = [this] { + switch (swkbd_config_common.initial_cursor_position) { + case SwkbdInitialCursorPosition::Start: + default: + return 0; + case SwkbdInitialCursorPosition::End: + return static_cast<s32>(initial_text.size()); + } + }(); + + const auto text_draw_type = [this, max_text_length] { + switch (swkbd_config_common.text_draw_type) { + case SwkbdTextDrawType::Line: + default: + return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; + case SwkbdTextDrawType::Box: + case SwkbdTextDrawType::DownloadCode: + return swkbd_config_common.text_draw_type; + } + }(); + + const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box + ? swkbd_config_common.enable_return_button + : false; + + const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 + ? swkbd_config_new.disable_cancel_button + : false; + + Core::Frontend::KeyboardInitializeParameters initialize_parameters{ + .ok_text{std::move(ok_text)}, + .header_text{std::move(header_text)}, + .sub_text{std::move(sub_text)}, + .guide_text{std::move(guide_text)}, + .initial_text{initial_text}, + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .initial_cursor_position{initial_cursor_position}, + .type{swkbd_config_common.type}, + .password_mode{swkbd_config_common.password_mode}, + .text_draw_type{text_draw_type}, + .key_disable_flags{swkbd_config_common.key_disable_flags}, + .use_blur_background{swkbd_config_common.use_blur_background}, + .enable_backspace_button{true}, + .enable_return_button{enable_return_button}, + .disable_cancel_button{disable_cancel_button}, + }; + + frontend.InitializeKeyboard(false, std::move(initialize_parameters), + [this](SwkbdResult result, std::u16string submitted_text) { + SubmitTextNormal(result, submitted_text); + }, + {}); + } +} + +void SoftwareKeyboard::ShowNormalKeyboard() { + frontend.ShowNormalKeyboard(); +} + +void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message) { + frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); +} + +void SoftwareKeyboard::ShowInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsHidden) { + return; + } + + ChangeState(SwkbdState::InitializedIsAppearing); + + const auto& appear_arg = swkbd_calc_arg.appear_arg; + + const u32 max_text_length = + appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH + ? appear_arg.max_text_length + : DEFAULT_MAX_TEXT_LENGTH; + + const u32 min_text_length = + appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; + + Core::Frontend::InlineAppearParameters appear_parameters{ + .max_text_length{max_text_length}, + .min_text_length{min_text_length}, + .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, + .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, + .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, + .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, + .type{appear_arg.type}, + .key_disable_flags{appear_arg.key_disable_flags}, + .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, + .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, + .enable_return_button{appear_arg.enable_return_button}, + .disable_cancel_button{appear_arg.disable_cancel_button}, + }; + + frontend.ShowInlineKeyboard(std::move(appear_parameters)); + + ChangeState(SwkbdState::InitializedIsShown); +} + +void SoftwareKeyboard::HideInlineKeyboard() { + if (swkbd_state != SwkbdState::InitializedIsShown) { + return; + } + + ChangeState(SwkbdState::InitializedIsDisappearing); + + frontend.HideInlineKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); +} + +void SoftwareKeyboard::InlineTextChanged() { + Core::Frontend::InlineTextParameters text_parameters{ + .input_text{current_text}, + .cursor_position{current_cursor_position}, + }; + + frontend.InlineTextChanged(std::move(text_parameters)); +} + +void SoftwareKeyboard::ExitKeyboard() { + complete = true; + status = ResultSuccess; + + frontend.ExitKeyboard(); + + broker.SignalStateChanged(); +} + +// Inline Software Keyboard Requests + +void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Finalize"); + + ChangeState(SwkbdState::NotInitialized); + + ExitKeyboard(); +} + +void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); +} + +void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); +} + +void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: Calc"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); + + std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), + sizeof(SwkbdCalcArg)); + + if (swkbd_calc_arg.flags.set_input_text) { + current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( + swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); + } + + if (swkbd_calc_arg.flags.set_cursor_position) { + current_cursor_position = swkbd_calc_arg.cursor_position; + } + + if (swkbd_calc_arg.flags.set_utf8_mode) { + inline_use_utf8 = swkbd_calc_arg.utf8_mode; + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_customize_dic) { + ReplyUnsetCustomizeDic(); + } + + if (swkbd_state <= SwkbdState::InitializedIsHidden && + swkbd_calc_arg.flags.unset_user_word_info) { + ReplyReleasedUserWordInfo(); + } + + if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { + InitializeFrontendKeyboard(); + + ChangeState(SwkbdState::InitializedIsHidden); + + ReplyFinishedInitialize(); + } + + if (!swkbd_calc_arg.flags.set_initialize_arg && + (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { + InlineTextChanged(); + } + + if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { + ShowInlineKeyboard(); + return; + } + + if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { + HideInlineKeyboard(); + return; + } +} + +void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); +} + +void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data) { + LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); + + ReplyUnsetCustomizedDictionaries(); +} + +void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data) { + LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); + + ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); + + std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); +} + +// Inline Software Keyboard Replies + +void SoftwareKeyboard::ReplyFinishedInitialize() { + LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); + + std::vector<u8> reply(REPLY_BASE_SIZE + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDefault() { + LOG_DEBUG(Service_AM, "Sending Reply: Default"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedString() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursor() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedTab() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); + + const SwkbdMovedTabArg moved_tab_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, + sizeof(SwkbdMovedTabArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnter() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast<u32>(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyDecidedCancel() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyDecidedEnterUtf8() { + LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdDecidedEnterArg decided_enter_arg{ + .text_length{static_cast<u32>(current_text.size())}, + }; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, + sizeof(SwkbdDecidedEnterArg)); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); + + HideInlineKeyboard(); +} + +void SoftwareKeyboard::ReplyUnsetCustomizeDic() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyReleasedUserWordInfo() { + LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { + LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); + + std::vector<u8> reply(REPLY_BASE_SIZE); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringV2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorV2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), + current_text.size() * sizeof(char16_t)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyChangedStringUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdChangedStringArg changed_string_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .dictionary_start_cursor_position{-1}, + .dictionary_end_cursor_position{-1}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, + sizeof(SwkbdChangedStringArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { + LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); + + std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); + + SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); + + std::string utf8_current_text = Common::UTF16ToUTF8(current_text); + + const SwkbdMovedCursorArg moved_cursor_arg{ + .text_length{static_cast<u32>(current_text.size())}, + .cursor_position{current_cursor_position}, + }; + + constexpr u8 flag = 0; + + std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, + sizeof(SwkbdMovedCursorArg)); + std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), + &flag, 1); + + broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); +} + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h new file mode 100644 index 000000000..9aef1bf11 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_software_keyboard.h @@ -0,0 +1,166 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_software_keyboard_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace Service::AM::Applets { + +class SoftwareKeyboard final : public Applet { +public: + explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, + Core::Frontend::SoftwareKeyboardApplet& frontend_); + ~SoftwareKeyboard() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + /** + * Submits the input text to the application. + * If text checking is enabled, the application will verify the input text. + * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the normal software keyboard. + * + * @param result SwkbdResult enum + * @param submitted_text UTF-16 encoded string + */ + void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); + + /** + * Submits the input text to the application. + * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. + * This should only be used by the inline software keyboard. + * + * @param reply_type SwkbdReplyType enum + * @param submitted_text UTF-16 encoded string + * @param cursor_position The current position of the text cursor + */ + void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, + s32 cursor_position); + +private: + /// Initializes the normal software keyboard. + void InitializeForeground(); + + /// Initializes the inline software keyboard. + void InitializeBackground(LibraryAppletMode library_applet_mode); + + /// Processes the text check sent by the application. + void ProcessTextCheck(); + + /// Processes the inline software keyboard request command sent by the application. + void ProcessInlineKeyboardRequest(); + + /// Submits the input text and exits the applet. + void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); + + /// Submits the input text for text checking. + void SubmitForTextCheck(std::u16string submitted_text); + + /// Sends a reply to the application after processing a request command. + void SendReply(SwkbdReplyType reply_type); + + /// Changes the inline keyboard state. + void ChangeState(SwkbdState state); + + /** + * Signals the frontend to initialize the software keyboard with common parameters. + * This initializes either the normal software keyboard or the inline software keyboard + * depending on the state of is_background. + * Note that this does not cause the keyboard to appear. + * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. + */ + void InitializeFrontendKeyboard(); + + /// Signals the frontend to show the normal software keyboard. + void ShowNormalKeyboard(); + + /// Signals the frontend to show the text check dialog. + void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, + std::u16string text_check_message); + + /// Signals the frontend to show the inline software keyboard. + void ShowInlineKeyboard(); + + /// Signals the frontend to hide the inline software keyboard. + void HideInlineKeyboard(); + + /// Signals the frontend that the current inline keyboard text has changed. + void InlineTextChanged(); + + /// Signals both the frontend and application that the software keyboard is exiting. + void ExitKeyboard(); + + // Inline Software Keyboard Requests + + void RequestFinalize(const std::vector<u8>& request_data); + void RequestSetUserWordInfo(const std::vector<u8>& request_data); + void RequestSetCustomizeDic(const std::vector<u8>& request_data); + void RequestCalc(const std::vector<u8>& request_data); + void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); + void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); + void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); + void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data); + + // Inline Software Keyboard Replies + + void ReplyFinishedInitialize(); + void ReplyDefault(); + void ReplyChangedString(); + void ReplyMovedCursor(); + void ReplyMovedTab(); + void ReplyDecidedEnter(); + void ReplyDecidedCancel(); + void ReplyChangedStringUtf8(); + void ReplyMovedCursorUtf8(); + void ReplyDecidedEnterUtf8(); + void ReplyUnsetCustomizeDic(); + void ReplyReleasedUserWordInfo(); + void ReplyUnsetCustomizedDictionaries(); + void ReplyChangedStringV2(); + void ReplyMovedCursorV2(); + void ReplyChangedStringUtf8V2(); + void ReplyMovedCursorUtf8V2(); + + Core::Frontend::SoftwareKeyboardApplet& frontend; + Core::System& system; + + SwkbdAppletVersion swkbd_applet_version; + + SwkbdConfigCommon swkbd_config_common; + SwkbdConfigOld swkbd_config_old; + SwkbdConfigOld2 swkbd_config_old2; + SwkbdConfigNew swkbd_config_new; + std::u16string initial_text; + + SwkbdState swkbd_state{SwkbdState::NotInitialized}; + SwkbdInitializeArg swkbd_initialize_arg; + SwkbdCalcArg swkbd_calc_arg; + bool use_changed_string_v2{false}; + bool use_moved_cursor_v2{false}; + bool inline_use_utf8{false}; + s32 current_cursor_position{}; + + std::u16string current_text; + + bool is_background{false}; + + bool complete{false}; + ResultCode status{ResultSuccess}; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h index 21aa8e800..21aa8e800 100644 --- a/src/core/hle/service/am/applets/software_keyboard_types.h +++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp new file mode 100644 index 000000000..35f194961 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -0,0 +1,487 @@ +// 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/fs/file.h" +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/mode.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/system_archive.h" +#include "core/file_sys/vfs_vector.h" +#include "core/frontend/applets/web_browser.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/result.h" +#include "core/hle/service/am/am.h" +#include "core/hle/service/am/applets/applet_web_browser.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/ns/pl_u.h" + +namespace Service::AM::Applets { + +namespace { + +template <typename T> +void ParseRawValue(T& value, const std::vector<u8>& data) { + static_assert(std::is_trivially_copyable_v<T>, + "It's undefined behavior to use memcpy with non-trivially copyable objects"); + std::memcpy(&value, data.data(), data.size()); +} + +template <typename T> +T ParseRawValue(const std::vector<u8>& data) { + T value; + ParseRawValue(value, data); + return value; +} + +std::string ParseStringValue(const std::vector<u8>& data) { + return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast<const char*>(data.data()), + data.size()); +} + +std::string GetMainURL(const std::string& url) { + const auto index = url.find('?'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index); +} + +std::string ResolveURL(const std::string& url) { + const auto index = url.find_first_of('%'); + + if (index == std::string::npos) { + return url; + } + + return url.substr(0, index) + "lp1" + url.substr(index + 1); +} + +WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) { + std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); + + if (web_arg.size() == sizeof(WebArgHeader)) { + return {}; + } + + WebArgInputTLVMap input_tlv_map; + + u64 current_offset = sizeof(WebArgHeader); + + for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { + if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { + return input_tlv_map; + } + + WebArgInputTLV input_tlv; + std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); + + current_offset += sizeof(WebArgInputTLV); + + if (web_arg.size() < current_offset + input_tlv.arg_data_size) { + return input_tlv_map; + } + + std::vector<u8> data(input_tlv.arg_data_size); + std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); + + current_offset += input_tlv.arg_data_size; + + input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); + } + + return input_tlv_map; +} + +FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id, + FileSys::ContentRecordType nca_type) { + if (nca_type == FileSys::ContentRecordType::Data) { + const auto nca = + system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type); + + if (nca == nullptr) { + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the System NAND!", + nca_type, title_id); + return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } + + return nca->GetRomFS(); + } else { + const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); + + if (nca == nullptr) { + LOG_ERROR(Service_AM, + "NCA of type={} with title_id={:016X} is not found in the ContentProvider!", + nca_type, title_id); + return nullptr; + } + + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + system.GetContentProvider()}; + + return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type); + } +} + +void ExtractSharedFonts(Core::System& system) { + static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{ + "FontStandard.ttf", + "FontChineseSimplified.ttf", + "FontExtendedChineseSimplified.ttf", + "FontChineseTraditional.ttf", + "FontKorean.ttf", + "FontNintendoExtended.ttf", + "FontNintendoExtended2.ttf", + }; + + const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; + + for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { + const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; + + if (Common::FS::Exists(font_file_path)) { + continue; + } + + const auto font = NS::SHARED_FONTS[i]; + const auto font_title_id = static_cast<u64>(font.first); + + const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry( + font_title_id, FileSys::ContentRecordType::Data); + + FileSys::VirtualFile romfs; + + if (!nca) { + romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id); + } else { + romfs = nca->GetRomFS(); + } + + if (!romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!", + font_title_id); + continue; + } + + const auto extracted_romfs = FileSys::ExtractRomFS(romfs); + + if (!extracted_romfs) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!", + font_title_id); + continue; + } + + const auto font_file = extracted_romfs->GetFile(font.second); + + if (!font_file) { + LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!", + font_title_id, font.second); + continue; + } + + std::vector<u32> font_data_u32(font_file->GetSize() / sizeof(u32)); + font_file->ReadBytes<u32>(font_data_u32.data(), font_file->GetSize()); + + std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), + Common::swap32); + + std::vector<u8> decrypted_data(font_file->GetSize() - 8); + + NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data); + + FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>( + std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); + + const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); + + FileSys::VfsRawCopy(decrypted_font, out_file); + } +} + +} // namespace + +WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_) + : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {} + +WebBrowser::~WebBrowser() = default; + +void WebBrowser::Initialize() { + Applet::Initialize(); + + LOG_INFO(Service_AM, "Initializing Web Browser Applet."); + + LOG_DEBUG(Service_AM, + "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); + + web_applet_version = WebAppletVersion{common_args.library_version}; + + const auto web_arg_storage = broker.PopNormalDataToApplet(); + ASSERT(web_arg_storage != nullptr); + + const auto& web_arg = web_arg_storage->GetData(); + ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); + + web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); + + LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", + web_arg_header.total_tlv_entries, web_arg_header.shim_kind); + + ExtractSharedFonts(system); + + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + InitializeShop(); + break; + case ShimKind::Login: + InitializeLogin(); + break; + case ShimKind::Offline: + InitializeOffline(); + break; + case ShimKind::Share: + InitializeShare(); + break; + case ShimKind::Web: + InitializeWeb(); + break; + case ShimKind::Wifi: + InitializeWifi(); + break; + case ShimKind::Lobby: + InitializeLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + break; + } +} + +bool WebBrowser::TransactionComplete() const { + return complete; +} + +ResultCode WebBrowser::GetStatus() const { + return status; +} + +void WebBrowser::ExecuteInteractive() { + UNIMPLEMENTED_MSG("WebSession is not implemented"); +} + +void WebBrowser::Execute() { + switch (web_arg_header.shim_kind) { + case ShimKind::Shop: + ExecuteShop(); + break; + case ShimKind::Login: + ExecuteLogin(); + break; + case ShimKind::Offline: + ExecuteOffline(); + break; + case ShimKind::Share: + ExecuteShare(); + break; + case ShimKind::Web: + ExecuteWeb(); + break; + case ShimKind::Wifi: + ExecuteWifi(); + break; + case ShimKind::Lobby: + ExecuteLobby(); + break; + default: + UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); + WebBrowserExit(WebExitReason::EndButtonPressed); + break; + } +} + +void WebBrowser::ExtractOfflineRomFS() { + LOG_DEBUG(Service_AM, "Extracting RomFS to {}", + Common::FS::PathToUTF8String(offline_cache_dir)); + + const auto extracted_romfs_dir = + FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + + const auto temp_dir = system.GetFilesystem()->CreateDirectory( + Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); + + FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); +} + +void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { + if ((web_arg_header.shim_kind == ShimKind::Share && + web_applet_version >= WebAppletVersion::Version196608) || + (web_arg_header.shim_kind == ShimKind::Web && + web_applet_version >= WebAppletVersion::Version524288)) { + // TODO: Push Output TLVs instead of a WebCommonReturnValue + } + + WebCommonReturnValue web_common_return_value; + + web_common_return_value.exit_reason = exit_reason; + std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); + web_common_return_value.last_url_size = last_url.size(); + + LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", + exit_reason, last_url, last_url.size()); + + complete = true; + std::vector<u8> out_data(sizeof(WebCommonReturnValue)); + std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); + broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); + broker.SignalStateChanged(); +} + +bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const { + return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end(); +} + +std::optional<std::vector<u8>> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) { + const auto map_it = web_arg_input_tlv_map.find(input_tlv_type); + + if (map_it == web_arg_input_tlv_map.end()) { + return std::nullopt; + } + + return map_it->second; +} + +void WebBrowser::InitializeShop() {} + +void WebBrowser::InitializeLogin() {} + +void WebBrowser::InitializeOffline() { + const auto document_path = + ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value()); + + const auto document_kind = + ParseRawValue<DocumentKind>(GetInputTLVData(WebArgInputTLVType::DocumentKind).value()); + + std::string additional_paths; + + switch (document_kind) { + case DocumentKind::OfflineHtmlPage: + default: + title_id = system.CurrentProcess()->GetTitleID(); + nca_type = FileSys::ContentRecordType::HtmlDocument; + additional_paths = "html-document"; + break; + case DocumentKind::ApplicationLegalInformation: + title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); + nca_type = FileSys::ContentRecordType::LegalInformation; + break; + case DocumentKind::SystemDataPage: + title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); + nca_type = FileSys::ContentRecordType::Data; + break; + } + + static constexpr std::array<const char*, 3> RESOURCE_TYPES{ + "manual", + "legal_information", + "system_data", + }; + + offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / + fmt::format("offline_web_applet_{}/{:016X}", + RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id); + + offline_document = Common::FS::ConcatPathSafe( + offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); +} + +void WebBrowser::InitializeShare() {} + +void WebBrowser::InitializeWeb() { + external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); + + // Resolve Nintendo CDN URLs. + external_url = ResolveURL(external_url); +} + +void WebBrowser::InitializeWifi() {} + +void WebBrowser::InitializeLobby() {} + +void WebBrowser::ExecuteShop() { + LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLogin() { + LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteOffline() { + const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); + + if (!Common::FS::Exists(main_url)) { + offline_romfs = GetOfflineRomFS(system, title_id, nca_type); + + if (offline_romfs == nullptr) { + LOG_ERROR(Service_AM, + "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id, + nca_type); + WebBrowserExit(WebExitReason::WindowClosed); + return; + } + } + + LOG_INFO(Service_AM, "Opening offline document at {}", + Common::FS::PathToUTF8String(offline_document)); + + frontend.OpenLocalWebPage( + Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteShare() { + LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteWeb() { + LOG_INFO(Service_AM, "Opening external URL at {}", external_url); + + frontend.OpenExternalWebPage(external_url, + [this](WebExitReason exit_reason, std::string last_url) { + WebBrowserExit(exit_reason, last_url); + }); +} + +void WebBrowser::ExecuteWifi() { + LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} + +void WebBrowser::ExecuteLobby() { + LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); + WebBrowserExit(WebExitReason::EndButtonPressed); +} +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h new file mode 100644 index 000000000..4f9e81b79 --- /dev/null +++ b/src/core/hle/service/am/applets/applet_web_browser.h @@ -0,0 +1,88 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <filesystem> +#include <optional> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" +#include "core/hle/result.h" +#include "core/hle/service/am/applets/applet_web_browser_types.h" +#include "core/hle/service/am/applets/applets.h" + +namespace Core { +class System; +} + +namespace FileSys { +enum class ContentRecordType : u8; +} + +namespace Service::AM::Applets { + +class WebBrowser final : public Applet { +public: + WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, + const Core::Frontend::WebBrowserApplet& frontend_); + + ~WebBrowser() override; + + void Initialize() override; + + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void ExtractOfflineRomFS(); + + void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); + +private: + bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const; + + std::optional<std::vector<u8>> GetInputTLVData(WebArgInputTLVType input_tlv_type); + + // Initializers for the various types of browser applets + void InitializeShop(); + void InitializeLogin(); + void InitializeOffline(); + void InitializeShare(); + void InitializeWeb(); + void InitializeWifi(); + void InitializeLobby(); + + // Executors for the various types of browser applets + void ExecuteShop(); + void ExecuteLogin(); + void ExecuteOffline(); + void ExecuteShare(); + void ExecuteWeb(); + void ExecuteWifi(); + void ExecuteLobby(); + + const Core::Frontend::WebBrowserApplet& frontend; + + bool complete{false}; + ResultCode status{ResultSuccess}; + + WebAppletVersion web_applet_version{}; + WebArgHeader web_arg_header{}; + WebArgInputTLVMap web_arg_input_tlv_map; + + u64 title_id{}; + FileSys::ContentRecordType nca_type{}; + std::filesystem::path offline_cache_dir; + std::filesystem::path offline_document; + FileSys::VirtualFile offline_romfs; + + std::string external_url; + + Core::System& system; +}; + +} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h index 419c2bf79..419c2bf79 100644 --- a/src/core/hle/service/am/applets/web_types.h +++ b/src/core/hle/service/am/applets/applet_web_browser_types.h diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index ae995df6b..2b7685d42 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -17,13 +17,13 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/applets/applet_controller.h" +#include "core/hle/service/am/applets/applet_error.h" +#include "core/hle/service/am/applets/applet_general_backend.h" +#include "core/hle/service/am/applets/applet_profile_select.h" +#include "core/hle/service/am/applets/applet_software_keyboard.h" +#include "core/hle/service/am/applets/applet_web_browser.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" -#include "core/hle/service/am/applets/software_keyboard.h" -#include "core/hle/service/am/applets/web_browser.h" #include "core/hle/service/sm/sm.h" namespace Service::AM::Applets { diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp deleted file mode 100644 index 218c8d1e4..000000000 --- a/src/core/hle/service/am/applets/controller.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// 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::NpadStyleSet 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 = std::move(identification_colors), - .enable_explain_text = enable_text, - .explain_text = std::move(text), - .allow_pro_controller = npad_style_set.fullkey == 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_, LibraryAppletMode applet_mode_, - const Core::Frontend::ControllerApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -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); - - controller_applet_version = ControllerAppletVersion{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(), private_arg.size()); - ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), - "Unknown ControllerSupportArgPrivate revision={} with size={}", - controller_applet_version, controller_private_arg.arg_private_size); - - // Some games such as Cave Story+ set invalid values for the ControllerSupportMode. - // Defer to arg_size to set the ControllerSupportMode. - if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) { - switch (controller_private_arg.arg_size) { - case sizeof(ControllerSupportArgOld): - case sizeof(ControllerSupportArgNew): - controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; - break; - case sizeof(ControllerUpdateFirmwareArg): - controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate; - break; - default: - UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}", - controller_private_arg.mode, controller_private_arg.arg_size); - controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport; - break; - } - } - - // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller. - // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem. - if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) { - if (controller_private_arg.flag_1 && - controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) { - controller_private_arg.caller = ControllerSupportCaller::System; - } else { - controller_private_arg.caller = ControllerSupportCaller::Application; - } - } - - switch (controller_private_arg.mode) { - case ControllerSupportMode::ShowControllerSupport: - case ControllerSupportMode::ShowControllerStrapGuide: { - const auto user_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(user_arg_storage != nullptr); - - const auto& user_arg = user_arg_storage->GetData(); - switch (controller_applet_version) { - case ControllerAppletVersion::Version3: - case ControllerAppletVersion::Version4: - case ControllerAppletVersion::Version5: - ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); - std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size()); - break; - case ControllerAppletVersion::Version7: - ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); - std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size()); - break; - default: - UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", - controller_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::ShowControllerFirmwareUpdate: { - const auto update_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(update_arg_storage != nullptr); - - const auto& update_arg = update_arg_storage->GetData(); - ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg)); - - std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size()); - break; - } - 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 (controller_applet_version) { - case ControllerAppletVersion::Version3: - case ControllerAppletVersion::Version4: - case ControllerAppletVersion::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 ControllerAppletVersion::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: - UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented", - controller_private_arg.mode); - ConfigurationComplete(); - break; - default: { - ConfigurationComplete(); - break; - } - } -} - -void Controller::ConfigurationComplete() { - ControllerSupportResultInfo result_info{}; - - const auto& players = Settings::values.players.GetValue(); - - // 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, - [](const auto& player) { return player.connected; })); - - result_info.selected_id = HID::Controller_NPad::IndexToNPad(std::distance( - players.begin(), std::find_if(players.begin(), players.end(), - [](const auto& 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>(system, 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 deleted file mode 100644 index 20617e91f..000000000 --- a/src/core/hle/service/am/applets/controller.h +++ /dev/null @@ -1,137 +0,0 @@ -// 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 ControllerAppletVersion : 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, - ShowControllerStrapGuide, - ShowControllerFirmwareUpdate, - - MaxControllerSupportMode, -}; - -enum class ControllerSupportCaller : u8 { - Application, - System, - - MaxControllerSupportCaller, -}; - -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 ControllerUpdateFirmwareArg { - bool enable_force_update{}; - INSERT_PADDING_BYTES(3); -}; -static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4, - "ControllerUpdateFirmwareArg 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_, LibraryAppletMode applet_mode_, - 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; - Core::System& system; - - ControllerAppletVersion controller_applet_version; - ControllerSupportArgPrivate controller_private_arg; - ControllerSupportArgOld controller_user_arg_old; - ControllerSupportArgNew controller_user_arg_new; - ControllerUpdateFirmwareArg controller_update_arg; - bool complete{false}; - ResultCode status{ResultSuccess}; - bool is_single_mode{false}; - std::vector<u8> out_data; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp deleted file mode 100644 index c724e5d5b..000000000 --- a/src/core/hle/service/am/applets/error.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <cstring> -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/error.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/error.h" -#include "core/reporter.h" - -namespace Service::AM::Applets { - -#pragma pack(push, 4) -struct ShowError { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(4); - bool use_64bit_error_code; - INSERT_PADDING_BYTES_NOINIT(1); - u64 error_code_64; - u32 error_code_32; -}; -static_assert(sizeof(ShowError) == 0x14, "ShowError has incorrect size."); -#pragma pack(pop) - -struct ShowErrorRecord { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u64 error_code_64; - u64 posix_time; -}; -static_assert(sizeof(ShowErrorRecord) == 0x18, "ShowErrorRecord has incorrect size."); - -struct SystemErrorArg { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u64 error_code_64; - std::array<char, 8> language_code; - std::array<char, 0x800> main_text; - std::array<char, 0x800> detail_text; -}; -static_assert(sizeof(SystemErrorArg) == 0x1018, "SystemErrorArg has incorrect size."); - -struct ApplicationErrorArg { - u8 mode; - bool jump; - INSERT_PADDING_BYTES_NOINIT(6); - u32 error_code; - std::array<char, 8> language_code; - std::array<char, 0x800> main_text; - std::array<char, 0x800> detail_text; -}; -static_assert(sizeof(ApplicationErrorArg) == 0x1014, "ApplicationErrorArg has incorrect size."); - -union Error::ErrorArguments { - ShowError error; - ShowErrorRecord error_record; - SystemErrorArg system_error; - ApplicationErrorArg application_error; - std::array<u8, 0x1018> raw{}; -}; - -namespace { -template <typename T> -void CopyArgumentData(const std::vector<u8>& data, T& variable) { - ASSERT(data.size() >= sizeof(T)); - std::memcpy(&variable, data.data(), sizeof(T)); -} - -ResultCode Decode64BitError(u64 error) { - const auto description = (error >> 32) & 0x1FFF; - auto module = error & 0x3FF; - if (module >= 2000) - module -= 2000; - module &= 0x1FF; - return {static_cast<ErrorModule>(module), static_cast<u32>(description)}; -} - -} // Anonymous namespace - -Error::Error(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ErrorApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -Error::~Error() = default; - -void Error::Initialize() { - Applet::Initialize(); - args = std::make_unique<ErrorArguments>(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - - ASSERT(!data.empty()); - std::memcpy(&mode, data.data(), sizeof(ErrorAppletMode)); - - switch (mode) { - case ErrorAppletMode::ShowError: - CopyArgumentData(data, args->error); - if (args->error.use_64bit_error_code) { - error_code = Decode64BitError(args->error.error_code_64); - } else { - error_code = ResultCode(args->error.error_code_32); - } - break; - case ErrorAppletMode::ShowSystemError: - CopyArgumentData(data, args->system_error); - error_code = ResultCode(Decode64BitError(args->system_error.error_code_64)); - break; - case ErrorAppletMode::ShowApplicationError: - CopyArgumentData(data, args->application_error); - error_code = ResultCode(args->application_error.error_code); - break; - case ErrorAppletMode::ShowErrorRecord: - CopyArgumentData(data, args->error_record); - error_code = Decode64BitError(args->error_record.error_code_64); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); - } -} - -bool Error::TransactionComplete() const { - return complete; -} - -ResultCode Error::GetStatus() const { - return ResultSuccess; -} - -void Error::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data!"); -} - -void Error::Execute() { - if (complete) { - return; - } - - const auto callback = [this] { DisplayCompleted(); }; - const auto title_id = system.CurrentProcess()->GetTitleID(); - const auto& reporter{system.GetReporter()}; - - switch (mode) { - case ErrorAppletMode::ShowError: - reporter.SaveErrorReport(title_id, error_code); - frontend.ShowError(error_code, callback); - break; - case ErrorAppletMode::ShowSystemError: - case ErrorAppletMode::ShowApplicationError: { - const auto is_system = mode == ErrorAppletMode::ShowSystemError; - const auto& main_text = - is_system ? args->system_error.main_text : args->application_error.main_text; - const auto& detail_text = - is_system ? args->system_error.detail_text : args->application_error.detail_text; - - const auto main_text_string = - Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()); - const auto detail_text_string = - Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()); - - reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string); - frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback); - break; - } - case ErrorAppletMode::ShowErrorRecord: - reporter.SaveErrorReport(title_id, error_code, - fmt::format("{:016X}", args->error_record.posix_time)); - frontend.ShowErrorWithTimestamp( - error_code, std::chrono::seconds{args->error_record.posix_time}, callback); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); - DisplayCompleted(); - } -} - -void Error::DisplayCompleted() { - complete = true; - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp deleted file mode 100644 index 9fcb9f95b..000000000 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <string_view> - -#include "common/assert.h" -#include "common/hex_util.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/frontend/applets/general_frontend.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/result.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/general_backend.h" -#include "core/reporter.h" - -namespace Service::AM::Applets { - -constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; - -static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { - std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); - for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { - const auto data = storage->GetData(); - LOG_INFO(Service_AM, - "called (STUBBED), during {} received normal data with size={:08X}, data={}", - prefix, data.size(), Common::HexToString(data)); - } - - storage = broker.PopInteractiveDataToApplet(); - for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) { - const auto data = storage->GetData(); - LOG_INFO(Service_AM, - "called (STUBBED), during {} received interactive data with size={:08X}, data={}", - prefix, data.size(), Common::HexToString(data)); - } -} - -Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::ParentalControlsApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -Auth::~Auth() = default; - -void Auth::Initialize() { - Applet::Initialize(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - ASSERT(data.size() >= 0xC); - - struct Arg { - INSERT_PADDING_BYTES(4); - AuthAppletType type; - u8 arg0; - u8 arg1; - u8 arg2; - INSERT_PADDING_BYTES(1); - }; - static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size."); - - Arg arg{}; - std::memcpy(&arg, data.data(), sizeof(Arg)); - - type = arg.type; - arg0 = arg.arg0; - arg1 = arg.arg1; - arg2 = arg.arg2; -} - -bool Auth::TransactionComplete() const { - return complete; -} - -ResultCode Auth::GetStatus() const { - return successful ? ResultSuccess : ERROR_INVALID_PIN; -} - -void Auth::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); -} - -void Auth::Execute() { - if (complete) { - return; - } - - const auto unimplemented_log = [this] { - UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, " - "arg1={:02X}, arg2={:02X}", - type, arg0, arg1, arg2); - }; - - switch (type) { - case AuthAppletType::ShowParentalAuthentication: { - const auto callback = [this](bool is_successful) { AuthFinished(is_successful); }; - - if (arg0 == 1 && arg1 == 0 && arg2 == 1) { - // ShowAuthenticatorForConfiguration - frontend.VerifyPINForSettings(callback); - } else if (arg1 == 0 && arg2 == 0) { - // ShowParentalAuthentication(bool) - frontend.VerifyPIN(callback, static_cast<bool>(arg0)); - } else { - unimplemented_log(); - } - break; - } - case AuthAppletType::RegisterParentalPasscode: { - const auto callback = [this] { AuthFinished(true); }; - - if (arg0 == 0 && arg1 == 0 && arg2 == 0) { - // RegisterParentalPasscode - frontend.RegisterPIN(callback); - } else { - unimplemented_log(); - } - break; - } - case AuthAppletType::ChangeParentalPasscode: { - const auto callback = [this] { AuthFinished(true); }; - - if (arg0 == 0 && arg1 == 0 && arg2 == 0) { - // ChangeParentalPasscode - frontend.ChangePIN(callback); - } else { - unimplemented_log(); - } - break; - } - default: - unimplemented_log(); - } -} - -void Auth::AuthFinished(bool is_successful) { - successful = is_successful; - - struct Return { - ResultCode result_code; - }; - static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size."); - - Return return_{GetStatus()}; - - std::vector<u8> out(sizeof(Return)); - std::memcpy(out.data(), &return_, sizeof(Return)); - - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out))); - broker.SignalStateChanged(); -} - -PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::PhotoViewerApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -PhotoViewer::~PhotoViewer() = default; - -void PhotoViewer::Initialize() { - Applet::Initialize(); - complete = false; - - const auto storage = broker.PopNormalDataToApplet(); - ASSERT(storage != nullptr); - const auto data = storage->GetData(); - ASSERT(!data.empty()); - mode = static_cast<PhotoViewerAppletMode>(data[0]); -} - -bool PhotoViewer::TransactionComplete() const { - return complete; -} - -ResultCode PhotoViewer::GetStatus() const { - return ResultSuccess; -} - -void PhotoViewer::ExecuteInteractive() { - UNREACHABLE_MSG("Unexpected interactive applet data."); -} - -void PhotoViewer::Execute() { - if (complete) - return; - - const auto callback = [this] { ViewFinished(); }; - switch (mode) { - case PhotoViewerAppletMode::CurrentApp: - frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); - break; - case PhotoViewerAppletMode::AllApps: - frontend.ShowAllPhotos(callback); - break; - default: - UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); - } -} - -void PhotoViewer::ViewFinished() { - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{})); - broker.SignalStateChanged(); -} - -StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_) - : Applet{system_, applet_mode_}, id{id_}, system{system_} {} - -StubApplet::~StubApplet() = default; - -void StubApplet::Initialize() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - Applet::Initialize(); - - const auto data = broker.PeekDataToAppletForDebug(); - system.GetReporter().SaveUnimplementedAppletReport( - static_cast<u32>(id), common_args.arguments_version, common_args.library_version, - common_args.theme_color, common_args.play_startup_sound, common_args.system_tick, - data.normal, data.interactive); - - LogCurrentStorage(broker, "Initialize"); -} - -bool StubApplet::TransactionComplete() const { - LOG_WARNING(Service_AM, "called (STUBBED)"); - return true; -} - -ResultCode StubApplet::GetStatus() const { - LOG_WARNING(Service_AM, "called (STUBBED)"); - return ResultSuccess; -} - -void StubApplet::ExecuteInteractive() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(broker, "ExecuteInteractive"); - - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); - broker.PushInteractiveDataFromApplet( - std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); - broker.SignalStateChanged(); -} - -void StubApplet::Execute() { - LOG_WARNING(Service_AM, "called (STUBBED)"); - LogCurrentStorage(broker, "Execute"); - - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); - broker.PushInteractiveDataFromApplet( - std::make_shared<IStorage>(system, std::vector<u8>(0x1000))); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp deleted file mode 100644 index 37048be26..000000000 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> - -#include "common/assert.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/profile_select.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/profile_select.h" - -namespace Service::AM::Applets { - -constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; - -ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::ProfileSelectApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -ProfileSelect::~ProfileSelect() = default; - -void ProfileSelect::Initialize() { - complete = false; - status = ResultSuccess; - final_data.clear(); - - Applet::Initialize(); - - const auto user_config_storage = broker.PopNormalDataToApplet(); - ASSERT(user_config_storage != nullptr); - const auto& user_config = user_config_storage->GetData(); - - ASSERT(user_config.size() >= sizeof(UserSelectionConfig)); - std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig)); -} - -bool ProfileSelect::TransactionComplete() const { - return complete; -} - -ResultCode ProfileSelect::GetStatus() const { - return status; -} - -void ProfileSelect::ExecuteInteractive() { - UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); -} - -void ProfileSelect::Execute() { - if (complete) { - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); - return; - } - - frontend.SelectProfile([this](std::optional<Common::UUID> uuid) { SelectionComplete(uuid); }); -} - -void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { - UserSelectionOutput output{}; - - if (uuid.has_value() && uuid->uuid != Common::INVALID_UUID) { - output.result = 0; - output.uuid_selected = uuid->uuid; - } else { - status = ERR_USER_CANCELLED_SELECTION; - output.result = ERR_USER_CANCELLED_SELECTION.raw; - output.uuid_selected = Common::INVALID_UUID; - } - - final_data = std::vector<u8>(sizeof(UserSelectionOutput)); - std::memcpy(final_data.data(), &output, final_data.size()); - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data))); - broker.SignalStateChanged(); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp deleted file mode 100644 index 00dfe1675..000000000 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/string_util.h" -#include "core/core.h" -#include "core/frontend/applets/software_keyboard.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/software_keyboard.h" - -namespace Service::AM::Applets { - -namespace { - -// The maximum number of UTF-16 characters that can be input into the swkbd text field. -constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500; - -constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType); -constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4; -constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC; - -constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) { - switch (text_check_result) { - case SwkbdTextCheckResult::Success: - return "Success"; - case SwkbdTextCheckResult::Failure: - return "Failure"; - case SwkbdTextCheckResult::Confirm: - return "Confirm"; - case SwkbdTextCheckResult::Silent: - return "Silent"; - default: - UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result); - return "Unknown"; - } -} - -void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply_type) { - std::memcpy(reply.data(), &state, sizeof(SwkbdState)); - std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType)); -} - -} // Anonymous namespace - -SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::SoftwareKeyboardApplet& frontend_) - : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {} - -SoftwareKeyboard::~SoftwareKeyboard() = default; - -void SoftwareKeyboard::Initialize() { - Applet::Initialize(); - - LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}", - applet_mode); - - LOG_DEBUG(Service_AM, - "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); - - swkbd_applet_version = SwkbdAppletVersion{common_args.library_version}; - - switch (applet_mode) { - case LibraryAppletMode::AllForeground: - InitializeForeground(); - break; - case LibraryAppletMode::Background: - case LibraryAppletMode::BackgroundIndirectDisplay: - InitializeBackground(applet_mode); - break; - default: - UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode); - break; - } -} - -bool SoftwareKeyboard::TransactionComplete() const { - return complete; -} - -ResultCode SoftwareKeyboard::GetStatus() const { - return status; -} - -void SoftwareKeyboard::ExecuteInteractive() { - if (complete) { - return; - } - - if (is_background) { - ProcessInlineKeyboardRequest(); - } else { - ProcessTextCheck(); - } -} - -void SoftwareKeyboard::Execute() { - if (complete) { - return; - } - - if (is_background) { - return; - } - - ShowNormalKeyboard(); -} - -void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) { - if (complete) { - return; - } - - if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) { - SubmitForTextCheck(submitted_text); - } else { - SubmitNormalOutputAndExit(result, submitted_text); - } -} - -void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, - s32 cursor_position) { - if (complete) { - return; - } - - current_text = std::move(submitted_text); - current_cursor_position = cursor_position; - - if (inline_use_utf8) { - switch (reply_type) { - case SwkbdReplyType::ChangedString: - reply_type = SwkbdReplyType::ChangedStringUtf8; - break; - case SwkbdReplyType::MovedCursor: - reply_type = SwkbdReplyType::MovedCursorUtf8; - break; - case SwkbdReplyType::DecidedEnter: - reply_type = SwkbdReplyType::DecidedEnterUtf8; - break; - default: - break; - } - } - - if (use_changed_string_v2) { - switch (reply_type) { - case SwkbdReplyType::ChangedString: - reply_type = SwkbdReplyType::ChangedStringV2; - break; - case SwkbdReplyType::ChangedStringUtf8: - reply_type = SwkbdReplyType::ChangedStringUtf8V2; - break; - default: - break; - } - } - - if (use_moved_cursor_v2) { - switch (reply_type) { - case SwkbdReplyType::MovedCursor: - reply_type = SwkbdReplyType::MovedCursorV2; - break; - case SwkbdReplyType::MovedCursorUtf8: - reply_type = SwkbdReplyType::MovedCursorUtf8V2; - break; - default: - break; - } - } - - SendReply(reply_type); -} - -void SoftwareKeyboard::InitializeForeground() { - LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet."); - - is_background = false; - - const auto swkbd_config_storage = broker.PopNormalDataToApplet(); - ASSERT(swkbd_config_storage != nullptr); - - const auto& swkbd_config_data = swkbd_config_storage->GetData(); - ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon)); - - std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon)); - - switch (swkbd_applet_version) { - case SwkbdAppletVersion::Version5: - case SwkbdAppletVersion::Version65542: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld)); - std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigOld)); - break; - case SwkbdAppletVersion::Version196615: - case SwkbdAppletVersion::Version262152: - case SwkbdAppletVersion::Version327689: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2)); - std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigOld2)); - break; - case SwkbdAppletVersion::Version393227: - case SwkbdAppletVersion::Version524301: - ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); - std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigNew)); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version, - swkbd_config_data.size()); - ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew)); - std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon), - sizeof(SwkbdConfigNew)); - break; - } - - const auto work_buffer_storage = broker.PopNormalDataToApplet(); - ASSERT(work_buffer_storage != nullptr); - - if (swkbd_config_common.initial_string_length == 0) { - InitializeFrontendKeyboard(); - return; - } - - const auto& work_buffer = work_buffer_storage->GetData(); - - std::vector<char16_t> initial_string(swkbd_config_common.initial_string_length); - - std::memcpy(initial_string.data(), - work_buffer.data() + swkbd_config_common.initial_string_offset, - swkbd_config_common.initial_string_length * sizeof(char16_t)); - - initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(), - initial_string.size()); - - LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); - - InitializeFrontendKeyboard(); -} - -void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { - LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet."); - - is_background = true; - - const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(swkbd_inline_initialize_arg_storage != nullptr); - - const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData(); - ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg)); - - std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(), - swkbd_inline_initialize_arg.size()); - - if (swkbd_initialize_arg.library_applet_mode_flag) { - ASSERT(library_applet_mode == LibraryAppletMode::Background); - } else { - ASSERT(library_applet_mode == LibraryAppletMode::BackgroundIndirectDisplay); - } -} - -void SoftwareKeyboard::ProcessTextCheck() { - const auto text_check_storage = broker.PopInteractiveDataToApplet(); - ASSERT(text_check_storage != nullptr); - - const auto& text_check_data = text_check_storage->GetData(); - ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck)); - - SwkbdTextCheck swkbd_text_check; - - std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck)); - - std::u16string text_check_message = - swkbd_text_check.text_check_result == SwkbdTextCheckResult::Failure || - swkbd_text_check.text_check_result == SwkbdTextCheckResult::Confirm - ? Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_text_check.text_check_message.data(), - swkbd_text_check.text_check_message.size()) - : u""; - - LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}", - GetTextCheckResultName(swkbd_text_check.text_check_result), - Common::UTF16ToUTF8(text_check_message)); - - switch (swkbd_text_check.text_check_result) { - case SwkbdTextCheckResult::Success: - SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text); - break; - case SwkbdTextCheckResult::Failure: - ShowTextCheckDialog(SwkbdTextCheckResult::Failure, std::move(text_check_message)); - break; - case SwkbdTextCheckResult::Confirm: - ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, std::move(text_check_message)); - break; - case SwkbdTextCheckResult::Silent: - default: - break; - } -} - -void SoftwareKeyboard::ProcessInlineKeyboardRequest() { - const auto request_data_storage = broker.PopInteractiveDataToApplet(); - ASSERT(request_data_storage != nullptr); - - const auto& request_data = request_data_storage->GetData(); - ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand)); - - SwkbdRequestCommand request_command; - - std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand)); - - switch (request_command) { - case SwkbdRequestCommand::Finalize: - RequestFinalize(request_data); - break; - case SwkbdRequestCommand::SetUserWordInfo: - RequestSetUserWordInfo(request_data); - break; - case SwkbdRequestCommand::SetCustomizeDic: - RequestSetCustomizeDic(request_data); - break; - case SwkbdRequestCommand::Calc: - RequestCalc(request_data); - break; - case SwkbdRequestCommand::SetCustomizedDictionaries: - RequestSetCustomizedDictionaries(request_data); - break; - case SwkbdRequestCommand::UnsetCustomizedDictionaries: - RequestUnsetCustomizedDictionaries(request_data); - break; - case SwkbdRequestCommand::SetChangedStringV2Flag: - RequestSetChangedStringV2Flag(request_data); - break; - case SwkbdRequestCommand::SetMovedCursorV2Flag: - RequestSetMovedCursorV2Flag(request_data); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command); - break; - } -} - -void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result, - std::u16string submitted_text) { - std::vector<u8> out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE); - - if (swkbd_config_common.use_utf8) { - std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text); - - LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result, - utf8_submitted_text); - - std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); - std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(), - utf8_submitted_text.size()); - } else { - LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result, - Common::UTF16ToUTF8(submitted_text)); - - std::memcpy(out_data.data(), &result, sizeof(SwkbdResult)); - std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(), - submitted_text.size() * sizeof(char16_t)); - } - - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); - - ExitKeyboard(); -} - -void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) { - current_text = std::move(submitted_text); - - std::vector<u8> out_data(sizeof(u64) + STRING_BUFFER_SIZE); - - if (swkbd_config_common.use_utf8) { - std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text); - const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size(); - - LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size, - utf8_submitted_text); - - std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); - std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(), - utf8_submitted_text.size()); - } else { - const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t); - - LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size, - Common::UTF16ToUTF8(current_text)); - - std::memcpy(out_data.data(), &buffer_size, sizeof(u64)); - std::memcpy(out_data.data() + sizeof(u64), current_text.data(), - current_text.size() * sizeof(char16_t)); - } - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); -} - -void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) { - switch (reply_type) { - case SwkbdReplyType::FinishedInitialize: - ReplyFinishedInitialize(); - break; - case SwkbdReplyType::Default: - ReplyDefault(); - break; - case SwkbdReplyType::ChangedString: - ReplyChangedString(); - break; - case SwkbdReplyType::MovedCursor: - ReplyMovedCursor(); - break; - case SwkbdReplyType::MovedTab: - ReplyMovedTab(); - break; - case SwkbdReplyType::DecidedEnter: - ReplyDecidedEnter(); - break; - case SwkbdReplyType::DecidedCancel: - ReplyDecidedCancel(); - break; - case SwkbdReplyType::ChangedStringUtf8: - ReplyChangedStringUtf8(); - break; - case SwkbdReplyType::MovedCursorUtf8: - ReplyMovedCursorUtf8(); - break; - case SwkbdReplyType::DecidedEnterUtf8: - ReplyDecidedEnterUtf8(); - break; - case SwkbdReplyType::UnsetCustomizeDic: - ReplyUnsetCustomizeDic(); - break; - case SwkbdReplyType::ReleasedUserWordInfo: - ReplyReleasedUserWordInfo(); - break; - case SwkbdReplyType::UnsetCustomizedDictionaries: - ReplyUnsetCustomizedDictionaries(); - break; - case SwkbdReplyType::ChangedStringV2: - ReplyChangedStringV2(); - break; - case SwkbdReplyType::MovedCursorV2: - ReplyMovedCursorV2(); - break; - case SwkbdReplyType::ChangedStringUtf8V2: - ReplyChangedStringUtf8V2(); - break; - case SwkbdReplyType::MovedCursorUtf8V2: - ReplyMovedCursorUtf8V2(); - break; - default: - UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type); - ReplyDefault(); - break; - } -} - -void SoftwareKeyboard::ChangeState(SwkbdState state) { - swkbd_state = state; - - ReplyDefault(); -} - -void SoftwareKeyboard::InitializeFrontendKeyboard() { - if (is_background) { - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - appear_arg.ok_text.data(), appear_arg.ok_text.size()); - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - const s32 initial_cursor_position = - current_cursor_position > 0 ? current_cursor_position : 0; - - const auto text_draw_type = - max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{}, - .sub_text{}, - .guide_text{}, - .initial_text{current_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{appear_arg.type}, - .password_mode{SwkbdPasswordMode::Disabled}, - .text_draw_type{text_draw_type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .use_blur_background{false}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.InitializeKeyboard( - true, std::move(initialize_parameters), {}, - [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { - SubmitTextInline(reply_type, submitted_text, cursor_position); - }); - } else { - std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); - - std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); - - std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); - - std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); - - const u32 max_text_length = - swkbd_config_common.max_text_length > 0 && - swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? swkbd_config_common.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length - ? swkbd_config_common.min_text_length - : 0; - - const s32 initial_cursor_position = [this] { - switch (swkbd_config_common.initial_cursor_position) { - case SwkbdInitialCursorPosition::Start: - default: - return 0; - case SwkbdInitialCursorPosition::End: - return static_cast<s32>(initial_text.size()); - } - }(); - - const auto text_draw_type = [this, max_text_length] { - switch (swkbd_config_common.text_draw_type) { - case SwkbdTextDrawType::Line: - default: - return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; - case SwkbdTextDrawType::Box: - case SwkbdTextDrawType::DownloadCode: - return swkbd_config_common.text_draw_type; - } - }(); - - const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box - ? swkbd_config_common.enable_return_button - : false; - - const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 - ? swkbd_config_new.disable_cancel_button - : false; - - Core::Frontend::KeyboardInitializeParameters initialize_parameters{ - .ok_text{std::move(ok_text)}, - .header_text{std::move(header_text)}, - .sub_text{std::move(sub_text)}, - .guide_text{std::move(guide_text)}, - .initial_text{initial_text}, - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .initial_cursor_position{initial_cursor_position}, - .type{swkbd_config_common.type}, - .password_mode{swkbd_config_common.password_mode}, - .text_draw_type{text_draw_type}, - .key_disable_flags{swkbd_config_common.key_disable_flags}, - .use_blur_background{swkbd_config_common.use_blur_background}, - .enable_backspace_button{true}, - .enable_return_button{enable_return_button}, - .disable_cancel_button{disable_cancel_button}, - }; - - frontend.InitializeKeyboard(false, std::move(initialize_parameters), - [this](SwkbdResult result, std::u16string submitted_text) { - SubmitTextNormal(result, submitted_text); - }, - {}); - } -} - -void SoftwareKeyboard::ShowNormalKeyboard() { - frontend.ShowNormalKeyboard(); -} - -void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, - std::u16string text_check_message) { - frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); -} - -void SoftwareKeyboard::ShowInlineKeyboard() { - if (swkbd_state != SwkbdState::InitializedIsHidden) { - return; - } - - ChangeState(SwkbdState::InitializedIsAppearing); - - const auto& appear_arg = swkbd_calc_arg.appear_arg; - - const u32 max_text_length = - appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH - ? appear_arg.max_text_length - : DEFAULT_MAX_TEXT_LENGTH; - - const u32 min_text_length = - appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; - - Core::Frontend::InlineAppearParameters appear_parameters{ - .max_text_length{max_text_length}, - .min_text_length{min_text_length}, - .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, - .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, - .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, - .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, - .type{appear_arg.type}, - .key_disable_flags{appear_arg.key_disable_flags}, - .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, - .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, - .enable_return_button{appear_arg.enable_return_button}, - .disable_cancel_button{appear_arg.disable_cancel_button}, - }; - - frontend.ShowInlineKeyboard(std::move(appear_parameters)); - - ChangeState(SwkbdState::InitializedIsShown); -} - -void SoftwareKeyboard::HideInlineKeyboard() { - if (swkbd_state != SwkbdState::InitializedIsShown) { - return; - } - - ChangeState(SwkbdState::InitializedIsDisappearing); - - frontend.HideInlineKeyboard(); - - ChangeState(SwkbdState::InitializedIsHidden); -} - -void SoftwareKeyboard::InlineTextChanged() { - Core::Frontend::InlineTextParameters text_parameters{ - .input_text{current_text}, - .cursor_position{current_cursor_position}, - }; - - frontend.InlineTextChanged(std::move(text_parameters)); -} - -void SoftwareKeyboard::ExitKeyboard() { - complete = true; - status = ResultSuccess; - - frontend.ExitKeyboard(); - - broker.SignalStateChanged(); -} - -// Inline Software Keyboard Requests - -void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: Finalize"); - - ChangeState(SwkbdState::NotInitialized); - - ExitKeyboard(); -} - -void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { - LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); -} - -void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { - LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented."); -} - -void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: Calc"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); - - std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), - sizeof(SwkbdCalcArg)); - - if (swkbd_calc_arg.flags.set_input_text) { - current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( - swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); - } - - if (swkbd_calc_arg.flags.set_cursor_position) { - current_cursor_position = swkbd_calc_arg.cursor_position; - } - - if (swkbd_calc_arg.flags.set_utf8_mode) { - inline_use_utf8 = swkbd_calc_arg.utf8_mode; - } - - if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_customize_dic) { - ReplyUnsetCustomizeDic(); - } - - if (swkbd_state <= SwkbdState::InitializedIsHidden && - swkbd_calc_arg.flags.unset_user_word_info) { - ReplyReleasedUserWordInfo(); - } - - if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { - InitializeFrontendKeyboard(); - - ChangeState(SwkbdState::InitializedIsHidden); - - ReplyFinishedInitialize(); - } - - if (!swkbd_calc_arg.flags.set_initialize_arg && - (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { - InlineTextChanged(); - } - - if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { - ShowInlineKeyboard(); - return; - } - - if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { - HideInlineKeyboard(); - return; - } -} - -void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector<u8>& request_data) { - LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented."); -} - -void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data) { - LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries"); - - ReplyUnsetCustomizedDictionaries(); -} - -void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector<u8>& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); - - std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); -} - -void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data) { - LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag"); - - ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1); - - std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1); -} - -// Inline Software Keyboard Replies - -void SoftwareKeyboard::ReplyFinishedInitialize() { - LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize"); - - std::vector<u8> reply(REPLY_BASE_SIZE + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDefault() { - LOG_DEBUG(Service_AM, "Sending Reply: Default"); - - std::vector<u8> reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedString() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedString"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursor() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedTab() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedTab"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab); - - const SwkbdMovedTabArg moved_tab_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg, - sizeof(SwkbdMovedTabArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDecidedEnter() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter); - - const SwkbdDecidedEnterArg decided_enter_arg{ - .text_length{static_cast<u32>(current_text.size())}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg, - sizeof(SwkbdDecidedEnterArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyDecidedCancel() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel"); - - std::vector<u8> reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyChangedStringUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyDecidedEnterUtf8() { - LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg)); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdDecidedEnterArg decided_enter_arg{ - .text_length{static_cast<u32>(current_text.size())}, - }; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg, - sizeof(SwkbdDecidedEnterArg)); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); - - HideInlineKeyboard(); -} - -void SoftwareKeyboard::ReplyUnsetCustomizeDic() { - LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic"); - - std::vector<u8> reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyReleasedUserWordInfo() { - LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo"); - - std::vector<u8> reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() { - LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries"); - - std::vector<u8> reply(REPLY_BASE_SIZE); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedStringV2() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorV2() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(), - current_text.size() * sizeof(char16_t)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyChangedStringUtf8V2() { - LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdChangedStringArg changed_string_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .dictionary_start_cursor_position{-1}, - .dictionary_end_cursor_position{-1}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg, - sizeof(SwkbdChangedStringArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -void SoftwareKeyboard::ReplyMovedCursorUtf8V2() { - LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2"); - - std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1); - - SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2); - - std::string utf8_current_text = Common::UTF16ToUTF8(current_text); - - const SwkbdMovedCursorArg moved_cursor_arg{ - .text_length{static_cast<u32>(current_text.size())}, - .cursor_position{current_cursor_position}, - }; - - constexpr u8 flag = 0; - - std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size()); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg, - sizeof(SwkbdMovedCursorArg)); - std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg), - &flag, 1); - - broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply))); -} - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h deleted file mode 100644 index e3fc733d0..000000000 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2021 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/software_keyboard_types.h" - -namespace Core { -class System; -} - -namespace Service::AM::Applets { - -class SoftwareKeyboard final : public Applet { -public: - explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_, - Core::Frontend::SoftwareKeyboardApplet& frontend_); - ~SoftwareKeyboard() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - /** - * Submits the input text to the application. - * If text checking is enabled, the application will verify the input text. - * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted. - * This should only be used by the normal software keyboard. - * - * @param result SwkbdResult enum - * @param submitted_text UTF-16 encoded string - */ - void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text); - - /** - * Submits the input text to the application. - * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted. - * This should only be used by the inline software keyboard. - * - * @param reply_type SwkbdReplyType enum - * @param submitted_text UTF-16 encoded string - * @param cursor_position The current position of the text cursor - */ - void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text, - s32 cursor_position); - -private: - /// Initializes the normal software keyboard. - void InitializeForeground(); - - /// Initializes the inline software keyboard. - void InitializeBackground(LibraryAppletMode library_applet_mode); - - /// Processes the text check sent by the application. - void ProcessTextCheck(); - - /// Processes the inline software keyboard request command sent by the application. - void ProcessInlineKeyboardRequest(); - - /// Submits the input text and exits the applet. - void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text); - - /// Submits the input text for text checking. - void SubmitForTextCheck(std::u16string submitted_text); - - /// Sends a reply to the application after processing a request command. - void SendReply(SwkbdReplyType reply_type); - - /// Changes the inline keyboard state. - void ChangeState(SwkbdState state); - - /** - * Signals the frontend to initialize the software keyboard with common parameters. - * This initializes either the normal software keyboard or the inline software keyboard - * depending on the state of is_background. - * Note that this does not cause the keyboard to appear. - * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. - */ - void InitializeFrontendKeyboard(); - - /// Signals the frontend to show the normal software keyboard. - void ShowNormalKeyboard(); - - /// Signals the frontend to show the text check dialog. - void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result, - std::u16string text_check_message); - - /// Signals the frontend to show the inline software keyboard. - void ShowInlineKeyboard(); - - /// Signals the frontend to hide the inline software keyboard. - void HideInlineKeyboard(); - - /// Signals the frontend that the current inline keyboard text has changed. - void InlineTextChanged(); - - /// Signals both the frontend and application that the software keyboard is exiting. - void ExitKeyboard(); - - // Inline Software Keyboard Requests - - void RequestFinalize(const std::vector<u8>& request_data); - void RequestSetUserWordInfo(const std::vector<u8>& request_data); - void RequestSetCustomizeDic(const std::vector<u8>& request_data); - void RequestCalc(const std::vector<u8>& request_data); - void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); - void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); - void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); - void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data); - - // Inline Software Keyboard Replies - - void ReplyFinishedInitialize(); - void ReplyDefault(); - void ReplyChangedString(); - void ReplyMovedCursor(); - void ReplyMovedTab(); - void ReplyDecidedEnter(); - void ReplyDecidedCancel(); - void ReplyChangedStringUtf8(); - void ReplyMovedCursorUtf8(); - void ReplyDecidedEnterUtf8(); - void ReplyUnsetCustomizeDic(); - void ReplyReleasedUserWordInfo(); - void ReplyUnsetCustomizedDictionaries(); - void ReplyChangedStringV2(); - void ReplyMovedCursorV2(); - void ReplyChangedStringUtf8V2(); - void ReplyMovedCursorUtf8V2(); - - Core::Frontend::SoftwareKeyboardApplet& frontend; - Core::System& system; - - SwkbdAppletVersion swkbd_applet_version; - - SwkbdConfigCommon swkbd_config_common; - SwkbdConfigOld swkbd_config_old; - SwkbdConfigOld2 swkbd_config_old2; - SwkbdConfigNew swkbd_config_new; - std::u16string initial_text; - - SwkbdState swkbd_state{SwkbdState::NotInitialized}; - SwkbdInitializeArg swkbd_initialize_arg; - SwkbdCalcArg swkbd_calc_arg; - bool use_changed_string_v2{false}; - bool use_moved_cursor_v2{false}; - bool inline_use_utf8{false}; - s32 current_cursor_position{}; - - std::u16string current_text; - - bool is_background{false}; - - bool complete{false}; - ResultCode status{ResultSuccess}; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp deleted file mode 100644 index 3b28e829b..000000000 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ /dev/null @@ -1,474 +0,0 @@ -// 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/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/file_sys/content_archive.h" -#include "core/file_sys/mode.h" -#include "core/file_sys/nca_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/file_sys/registered_cache.h" -#include "core/file_sys/romfs.h" -#include "core/file_sys/system_archive/system_archive.h" -#include "core/file_sys/vfs_vector.h" -#include "core/frontend/applets/web_browser.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/result.h" -#include "core/hle/service/am/am.h" -#include "core/hle/service/am/applets/web_browser.h" -#include "core/hle/service/filesystem/filesystem.h" -#include "core/hle/service/ns/pl_u.h" - -namespace Service::AM::Applets { - -namespace { - -template <typename T> -void ParseRawValue(T& value, const std::vector<u8>& data) { - static_assert(std::is_trivially_copyable_v<T>, - "It's undefined behavior to use memcpy with non-trivially copyable objects"); - std::memcpy(&value, data.data(), data.size()); -} - -template <typename T> -T ParseRawValue(const std::vector<u8>& data) { - T value; - ParseRawValue(value, data); - return value; -} - -std::string ParseStringValue(const std::vector<u8>& data) { - return Common::StringFromFixedZeroTerminatedBuffer(reinterpret_cast<const char*>(data.data()), - data.size()); -} - -std::string GetMainURL(const std::string& url) { - const auto index = url.find('?'); - - if (index == std::string::npos) { - return url; - } - - return url.substr(0, index); -} - -WebArgInputTLVMap ReadWebArgs(const std::vector<u8>& web_arg, WebArgHeader& web_arg_header) { - std::memcpy(&web_arg_header, web_arg.data(), sizeof(WebArgHeader)); - - if (web_arg.size() == sizeof(WebArgHeader)) { - return {}; - } - - WebArgInputTLVMap input_tlv_map; - - u64 current_offset = sizeof(WebArgHeader); - - for (std::size_t i = 0; i < web_arg_header.total_tlv_entries; ++i) { - if (web_arg.size() < current_offset + sizeof(WebArgInputTLV)) { - return input_tlv_map; - } - - WebArgInputTLV input_tlv; - std::memcpy(&input_tlv, web_arg.data() + current_offset, sizeof(WebArgInputTLV)); - - current_offset += sizeof(WebArgInputTLV); - - if (web_arg.size() < current_offset + input_tlv.arg_data_size) { - return input_tlv_map; - } - - std::vector<u8> data(input_tlv.arg_data_size); - std::memcpy(data.data(), web_arg.data() + current_offset, input_tlv.arg_data_size); - - current_offset += input_tlv.arg_data_size; - - input_tlv_map.insert_or_assign(input_tlv.input_tlv_type, std::move(data)); - } - - return input_tlv_map; -} - -FileSys::VirtualFile GetOfflineRomFS(Core::System& system, u64 title_id, - FileSys::ContentRecordType nca_type) { - if (nca_type == FileSys::ContentRecordType::Data) { - const auto nca = - system.GetFileSystemController().GetSystemNANDContents()->GetEntry(title_id, nca_type); - - if (nca == nullptr) { - LOG_ERROR(Service_AM, - "NCA of type={} with title_id={:016X} is not found in the System NAND!", - nca_type, title_id); - return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); - } - - return nca->GetRomFS(); - } else { - const auto nca = system.GetContentProvider().GetEntry(title_id, nca_type); - - if (nca == nullptr) { - LOG_ERROR(Service_AM, - "NCA of type={} with title_id={:016X} is not found in the ContentProvider!", - nca_type, title_id); - return nullptr; - } - - const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), - system.GetContentProvider()}; - - return pm.PatchRomFS(nca->GetRomFS(), nca->GetBaseIVFCOffset(), nca_type); - } -} - -void ExtractSharedFonts(Core::System& system) { - static constexpr std::array<const char*, 7> DECRYPTED_SHARED_FONTS{ - "FontStandard.ttf", - "FontChineseSimplified.ttf", - "FontExtendedChineseSimplified.ttf", - "FontChineseTraditional.ttf", - "FontKorean.ttf", - "FontNintendoExtended.ttf", - "FontNintendoExtended2.ttf", - }; - - const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; - - for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { - const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; - - if (Common::FS::Exists(font_file_path)) { - continue; - } - - const auto font = NS::SHARED_FONTS[i]; - const auto font_title_id = static_cast<u64>(font.first); - - const auto nca = system.GetFileSystemController().GetSystemNANDContents()->GetEntry( - font_title_id, FileSys::ContentRecordType::Data); - - FileSys::VirtualFile romfs; - - if (!nca) { - romfs = FileSys::SystemArchive::SynthesizeSystemArchive(font_title_id); - } else { - romfs = nca->GetRomFS(); - } - - if (!romfs) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} cannot be extracted!", - font_title_id); - continue; - } - - const auto extracted_romfs = FileSys::ExtractRomFS(romfs); - - if (!extracted_romfs) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} failed to extract!", - font_title_id); - continue; - } - - const auto font_file = extracted_romfs->GetFile(font.second); - - if (!font_file) { - LOG_ERROR(Service_AM, "SharedFont RomFS with title_id={:016X} has no font file \"{}\"!", - font_title_id, font.second); - continue; - } - - std::vector<u32> font_data_u32(font_file->GetSize() / sizeof(u32)); - font_file->ReadBytes<u32>(font_data_u32.data(), font_file->GetSize()); - - std::transform(font_data_u32.begin(), font_data_u32.end(), font_data_u32.begin(), - Common::swap32); - - std::vector<u8> decrypted_data(font_file->GetSize() - 8); - - NS::DecryptSharedFontToTTF(font_data_u32, decrypted_data); - - FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>( - std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); - - const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); - - const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); - - FileSys::VfsRawCopy(decrypted_font, out_file); - } -} - -} // namespace - -WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::WebBrowserApplet& frontend_) - : Applet{system_, applet_mode_}, frontend(frontend_), system{system_} {} - -WebBrowser::~WebBrowser() = default; - -void WebBrowser::Initialize() { - Applet::Initialize(); - - LOG_INFO(Service_AM, "Initializing Web Browser Applet."); - - LOG_DEBUG(Service_AM, - "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); - - web_applet_version = WebAppletVersion{common_args.library_version}; - - const auto web_arg_storage = broker.PopNormalDataToApplet(); - ASSERT(web_arg_storage != nullptr); - - const auto& web_arg = web_arg_storage->GetData(); - ASSERT_OR_EXECUTE(web_arg.size() >= sizeof(WebArgHeader), { return; }); - - web_arg_input_tlv_map = ReadWebArgs(web_arg, web_arg_header); - - LOG_DEBUG(Service_AM, "WebArgHeader: total_tlv_entries={}, shim_kind={}", - web_arg_header.total_tlv_entries, web_arg_header.shim_kind); - - ExtractSharedFonts(system); - - switch (web_arg_header.shim_kind) { - case ShimKind::Shop: - InitializeShop(); - break; - case ShimKind::Login: - InitializeLogin(); - break; - case ShimKind::Offline: - InitializeOffline(); - break; - case ShimKind::Share: - InitializeShare(); - break; - case ShimKind::Web: - InitializeWeb(); - break; - case ShimKind::Wifi: - InitializeWifi(); - break; - case ShimKind::Lobby: - InitializeLobby(); - break; - default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); - break; - } -} - -bool WebBrowser::TransactionComplete() const { - return complete; -} - -ResultCode WebBrowser::GetStatus() const { - return status; -} - -void WebBrowser::ExecuteInteractive() { - UNIMPLEMENTED_MSG("WebSession is not implemented"); -} - -void WebBrowser::Execute() { - switch (web_arg_header.shim_kind) { - case ShimKind::Shop: - ExecuteShop(); - break; - case ShimKind::Login: - ExecuteLogin(); - break; - case ShimKind::Offline: - ExecuteOffline(); - break; - case ShimKind::Share: - ExecuteShare(); - break; - case ShimKind::Web: - ExecuteWeb(); - break; - case ShimKind::Wifi: - ExecuteWifi(); - break; - case ShimKind::Lobby: - ExecuteLobby(); - break; - default: - UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind); - WebBrowserExit(WebExitReason::EndButtonPressed); - break; - } -} - -void WebBrowser::ExtractOfflineRomFS() { - LOG_DEBUG(Service_AM, "Extracting RomFS to {}", - Common::FS::PathToUTF8String(offline_cache_dir)); - - const auto extracted_romfs_dir = - FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); - - const auto temp_dir = system.GetFilesystem()->CreateDirectory( - Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); - - FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); -} - -void WebBrowser::WebBrowserExit(WebExitReason exit_reason, std::string last_url) { - if ((web_arg_header.shim_kind == ShimKind::Share && - web_applet_version >= WebAppletVersion::Version196608) || - (web_arg_header.shim_kind == ShimKind::Web && - web_applet_version >= WebAppletVersion::Version524288)) { - // TODO: Push Output TLVs instead of a WebCommonReturnValue - } - - WebCommonReturnValue web_common_return_value; - - web_common_return_value.exit_reason = exit_reason; - std::memcpy(&web_common_return_value.last_url, last_url.data(), last_url.size()); - web_common_return_value.last_url_size = last_url.size(); - - LOG_DEBUG(Service_AM, "WebCommonReturnValue: exit_reason={}, last_url={}, last_url_size={}", - exit_reason, last_url, last_url.size()); - - complete = true; - std::vector<u8> out_data(sizeof(WebCommonReturnValue)); - std::memcpy(out_data.data(), &web_common_return_value, out_data.size()); - broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data))); - broker.SignalStateChanged(); -} - -bool WebBrowser::InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const { - return web_arg_input_tlv_map.find(input_tlv_type) != web_arg_input_tlv_map.end(); -} - -std::optional<std::vector<u8>> WebBrowser::GetInputTLVData(WebArgInputTLVType input_tlv_type) { - const auto map_it = web_arg_input_tlv_map.find(input_tlv_type); - - if (map_it == web_arg_input_tlv_map.end()) { - return std::nullopt; - } - - return map_it->second; -} - -void WebBrowser::InitializeShop() {} - -void WebBrowser::InitializeLogin() {} - -void WebBrowser::InitializeOffline() { - const auto document_path = - ParseStringValue(GetInputTLVData(WebArgInputTLVType::DocumentPath).value()); - - const auto document_kind = - ParseRawValue<DocumentKind>(GetInputTLVData(WebArgInputTLVType::DocumentKind).value()); - - std::string additional_paths; - - switch (document_kind) { - case DocumentKind::OfflineHtmlPage: - default: - title_id = system.CurrentProcess()->GetTitleID(); - nca_type = FileSys::ContentRecordType::HtmlDocument; - additional_paths = "html-document"; - break; - case DocumentKind::ApplicationLegalInformation: - title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::ApplicationID).value()); - nca_type = FileSys::ContentRecordType::LegalInformation; - break; - case DocumentKind::SystemDataPage: - title_id = ParseRawValue<u64>(GetInputTLVData(WebArgInputTLVType::SystemDataID).value()); - nca_type = FileSys::ContentRecordType::Data; - break; - } - - static constexpr std::array<const char*, 3> RESOURCE_TYPES{ - "manual", - "legal_information", - "system_data", - }; - - offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / - fmt::format("offline_web_applet_{}/{:016X}", - RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id); - - offline_document = Common::FS::ConcatPathSafe( - offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); -} - -void WebBrowser::InitializeShare() {} - -void WebBrowser::InitializeWeb() { - external_url = ParseStringValue(GetInputTLVData(WebArgInputTLVType::InitialURL).value()); -} - -void WebBrowser::InitializeWifi() {} - -void WebBrowser::InitializeLobby() {} - -void WebBrowser::ExecuteShop() { - LOG_WARNING(Service_AM, "(STUBBED) called, Shop Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteLogin() { - LOG_WARNING(Service_AM, "(STUBBED) called, Login Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteOffline() { - const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); - - if (!Common::FS::Exists(main_url)) { - offline_romfs = GetOfflineRomFS(system, title_id, nca_type); - - if (offline_romfs == nullptr) { - LOG_ERROR(Service_AM, - "RomFS with title_id={:016X} and nca_type={} cannot be extracted!", title_id, - nca_type); - WebBrowserExit(WebExitReason::WindowClosed); - return; - } - } - - LOG_INFO(Service_AM, "Opening offline document at {}", - Common::FS::PathToUTF8String(offline_document)); - - frontend.OpenLocalWebPage( - Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, - [this](WebExitReason exit_reason, std::string last_url) { - WebBrowserExit(exit_reason, last_url); - }); -} - -void WebBrowser::ExecuteShare() { - LOG_WARNING(Service_AM, "(STUBBED) called, Share Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteWeb() { - LOG_INFO(Service_AM, "Opening external URL at {}", external_url); - - frontend.OpenExternalWebPage(external_url, - [this](WebExitReason exit_reason, std::string last_url) { - WebBrowserExit(exit_reason, last_url); - }); -} - -void WebBrowser::ExecuteWifi() { - LOG_WARNING(Service_AM, "(STUBBED) called, Wifi Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} - -void WebBrowser::ExecuteLobby() { - LOG_WARNING(Service_AM, "(STUBBED) called, Lobby Applet is not implemented"); - WebBrowserExit(WebExitReason::EndButtonPressed); -} -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h deleted file mode 100644 index 9f81214b6..000000000 --- a/src/core/hle/service/am/applets/web_browser.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2020 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <filesystem> -#include <optional> - -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/file_sys/vfs_types.h" -#include "core/hle/result.h" -#include "core/hle/service/am/applets/applets.h" -#include "core/hle/service/am/applets/web_types.h" - -namespace Core { -class System; -} - -namespace FileSys { -enum class ContentRecordType : u8; -} - -namespace Service::AM::Applets { - -class WebBrowser final : public Applet { -public: - WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_, - const Core::Frontend::WebBrowserApplet& frontend_); - - ~WebBrowser() override; - - void Initialize() override; - - bool TransactionComplete() const override; - ResultCode GetStatus() const override; - void ExecuteInteractive() override; - void Execute() override; - - void ExtractOfflineRomFS(); - - void WebBrowserExit(WebExitReason exit_reason, std::string last_url = ""); - -private: - bool InputTLVExistsInMap(WebArgInputTLVType input_tlv_type) const; - - std::optional<std::vector<u8>> GetInputTLVData(WebArgInputTLVType input_tlv_type); - - // Initializers for the various types of browser applets - void InitializeShop(); - void InitializeLogin(); - void InitializeOffline(); - void InitializeShare(); - void InitializeWeb(); - void InitializeWifi(); - void InitializeLobby(); - - // Executors for the various types of browser applets - void ExecuteShop(); - void ExecuteLogin(); - void ExecuteOffline(); - void ExecuteShare(); - void ExecuteWeb(); - void ExecuteWifi(); - void ExecuteLobby(); - - const Core::Frontend::WebBrowserApplet& frontend; - - bool complete{false}; - ResultCode status{ResultSuccess}; - - WebAppletVersion web_applet_version{}; - WebArgHeader web_arg_header{}; - WebArgInputTLVMap web_arg_input_tlv_map; - - u64 title_id{}; - FileSys::ContentRecordType nca_type{}; - std::filesystem::path offline_cache_dir; - std::filesystem::path offline_document; - FileSys::VirtualFile offline_romfs; - - std::string external_url; - - Core::System& system; -}; - -} // namespace Service::AM::Applets diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 97d6619dd..f5ebfe8d6 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -5,7 +5,7 @@ #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/service/apm/apm.h" -#include "core/hle/service/apm/interface.h" +#include "core/hle/service/apm/apm_interface.h" namespace Service::APM { diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp new file mode 100644 index 000000000..98839fe97 --- /dev/null +++ b/src/core/hle/service/apm/apm_controller.cpp @@ -0,0 +1,89 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <utility> + +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/core_timing.h" +#include "core/hle/service/apm/apm_controller.h" + +namespace Service::APM { + +constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Config7; + +Controller::Controller(Core::Timing::CoreTiming& core_timing_) + : core_timing{core_timing_}, configs{ + {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, + {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, + } {} + +Controller::~Controller() = default; + +void Controller::SetPerformanceConfiguration(PerformanceMode mode, + PerformanceConfiguration config) { + static constexpr std::array<std::pair<PerformanceConfiguration, u32>, 16> config_to_speed{{ + {PerformanceConfiguration::Config1, 1020}, + {PerformanceConfiguration::Config2, 1020}, + {PerformanceConfiguration::Config3, 1224}, + {PerformanceConfiguration::Config4, 1020}, + {PerformanceConfiguration::Config5, 1020}, + {PerformanceConfiguration::Config6, 1224}, + {PerformanceConfiguration::Config7, 1020}, + {PerformanceConfiguration::Config8, 1020}, + {PerformanceConfiguration::Config9, 1020}, + {PerformanceConfiguration::Config10, 1020}, + {PerformanceConfiguration::Config11, 1020}, + {PerformanceConfiguration::Config12, 1020}, + {PerformanceConfiguration::Config13, 1785}, + {PerformanceConfiguration::Config14, 1785}, + {PerformanceConfiguration::Config15, 1020}, + {PerformanceConfiguration::Config16, 1020}, + }}; + + const auto iter = std::find_if(config_to_speed.cbegin(), config_to_speed.cend(), + [config](const auto& entry) { return entry.first == config; }); + + if (iter == config_to_speed.cend()) { + LOG_ERROR(Service_APM, "Invalid performance configuration value provided: {}", config); + return; + } + + SetClockSpeed(iter->second); + configs.insert_or_assign(mode, config); +} + +void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { + constexpr std::array<PerformanceConfiguration, 3> BOOST_MODE_TO_CONFIG_MAP{{ + PerformanceConfiguration::Config7, + PerformanceConfiguration::Config13, + PerformanceConfiguration::Config15, + }}; + + SetPerformanceConfiguration(PerformanceMode::Docked, + BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); +} + +PerformanceMode Controller::GetCurrentPerformanceMode() const { + return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked + : PerformanceMode::Handheld; +} + +PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { + if (configs.find(mode) == configs.end()) { + configs.insert_or_assign(mode, DEFAULT_PERFORMANCE_CONFIGURATION); + } + + return configs[mode]; +} + +void Controller::SetClockSpeed(u32 mhz) { + LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); + // TODO(DarkLordZach): Actually signal core_timing to change clock speed. + // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. +} + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/apm_controller.h index 8d48e0104..8d48e0104 100644 --- a/src/core/hle/service/apm/controller.h +++ b/src/core/hle/service/apm/apm_controller.h diff --git a/src/core/hle/service/apm/apm_interface.cpp b/src/core/hle/service/apm/apm_interface.cpp new file mode 100644 index 000000000..e58bad083 --- /dev/null +++ b/src/core/hle/service/apm/apm_interface.cpp @@ -0,0 +1,138 @@ +// Copyright 2018 yuzu emulator team +// 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/apm/apm.h" +#include "core/hle/service/apm/apm_controller.h" +#include "core/hle/service/apm/apm_interface.h" + +namespace Service::APM { + +class ISession final : public ServiceFramework<ISession> { +public: + explicit ISession(Core::System& system_, Controller& controller_) + : ServiceFramework{system_, "ISession"}, controller{controller_} { + static const FunctionInfo functions[] = { + {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, + {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, + {2, nullptr, "SetCpuOverclockEnabled"}, + }; + RegisterHandlers(functions); + } + +private: + void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto mode = rp.PopEnum<PerformanceMode>(); + const auto config = rp.PopEnum<PerformanceConfiguration>(); + LOG_DEBUG(Service_APM, "called mode={} config={}", mode, config); + + controller.SetPerformanceConfiguration(mode, config); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto mode = rp.PopEnum<PerformanceMode>(); + LOG_DEBUG(Service_APM, "called mode={}", mode); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(controller.GetCurrentPerformanceConfiguration(mode)); + } + + Controller& controller; +}; + +APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& controller_, + const char* name) + : ServiceFramework{system_, name}, apm(std::move(apm_)), controller{controller_} { + static const FunctionInfo functions[] = { + {0, &APM::OpenSession, "OpenSession"}, + {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, + {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"}, + }; + RegisterHandlers(functions); +} + +APM::~APM() = default; + +void APM::OpenSession(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISession>(system, controller); +} + +void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.PushEnum(controller.GetCurrentPerformanceMode()); +} + +void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_APM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +APM_Sys::APM_Sys(Core::System& system_, Controller& controller_) + : ServiceFramework{system_, "apm:sys"}, controller{controller_} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestPerformanceMode"}, + {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"}, + {2, nullptr, "GetThrottlingState"}, + {3, nullptr, "GetLastThrottlingState"}, + {4, nullptr, "ClearLastThrottlingState"}, + {5, nullptr, "LoadAndApplySettings"}, + {6, &APM_Sys::SetCpuBoostMode, "SetCpuBoostMode"}, + {7, &APM_Sys::GetCurrentPerformanceConfiguration, "GetCurrentPerformanceConfiguration"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +APM_Sys::~APM_Sys() = default; + +void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<ISession>(system, controller); +} + +void APM_Sys::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto mode = rp.PopEnum<CpuBoostMode>(); + + LOG_DEBUG(Service_APM, "called, mode={:08X}", mode); + + controller.SetFromCpuBoostMode(mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void APM_Sys::GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum( + controller.GetCurrentPerformanceConfiguration(controller.GetCurrentPerformanceMode())); +} + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/apm_interface.h index 063ad5308..063ad5308 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/apm_interface.h diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp deleted file mode 100644 index 8bfa7c0e4..000000000 --- a/src/core/hle/service/apm/controller.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <array> -#include <utility> - -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/hle/service/apm/controller.h" - -namespace Service::APM { - -constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Config7; - -Controller::Controller(Core::Timing::CoreTiming& core_timing_) - : core_timing{core_timing_}, configs{ - {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION}, - {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION}, - } {} - -Controller::~Controller() = default; - -void Controller::SetPerformanceConfiguration(PerformanceMode mode, - PerformanceConfiguration config) { - static constexpr std::array<std::pair<PerformanceConfiguration, u32>, 16> config_to_speed{{ - {PerformanceConfiguration::Config1, 1020}, - {PerformanceConfiguration::Config2, 1020}, - {PerformanceConfiguration::Config3, 1224}, - {PerformanceConfiguration::Config4, 1020}, - {PerformanceConfiguration::Config5, 1020}, - {PerformanceConfiguration::Config6, 1224}, - {PerformanceConfiguration::Config7, 1020}, - {PerformanceConfiguration::Config8, 1020}, - {PerformanceConfiguration::Config9, 1020}, - {PerformanceConfiguration::Config10, 1020}, - {PerformanceConfiguration::Config11, 1020}, - {PerformanceConfiguration::Config12, 1020}, - {PerformanceConfiguration::Config13, 1785}, - {PerformanceConfiguration::Config14, 1785}, - {PerformanceConfiguration::Config15, 1020}, - {PerformanceConfiguration::Config16, 1020}, - }}; - - const auto iter = std::find_if(config_to_speed.cbegin(), config_to_speed.cend(), - [config](const auto& entry) { return entry.first == config; }); - - if (iter == config_to_speed.cend()) { - LOG_ERROR(Service_APM, "Invalid performance configuration value provided: {}", config); - return; - } - - SetClockSpeed(iter->second); - configs.insert_or_assign(mode, config); -} - -void Controller::SetFromCpuBoostMode(CpuBoostMode mode) { - constexpr std::array<PerformanceConfiguration, 3> BOOST_MODE_TO_CONFIG_MAP{{ - PerformanceConfiguration::Config7, - PerformanceConfiguration::Config13, - PerformanceConfiguration::Config15, - }}; - - SetPerformanceConfiguration(PerformanceMode::Docked, - BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode))); -} - -PerformanceMode Controller::GetCurrentPerformanceMode() const { - return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked - : PerformanceMode::Handheld; -} - -PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) { - if (configs.find(mode) == configs.end()) { - configs.insert_or_assign(mode, DEFAULT_PERFORMANCE_CONFIGURATION); - } - - return configs[mode]; -} - -void Controller::SetClockSpeed(u32 mhz) { - LOG_INFO(Service_APM, "called, mhz={:08X}", mhz); - // TODO(DarkLordZach): Actually signal core_timing to change clock speed. - // TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used. -} - -} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp deleted file mode 100644 index d69ddd135..000000000 --- a/src/core/hle/service/apm/interface.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2018 yuzu emulator team -// 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/apm/apm.h" -#include "core/hle/service/apm/controller.h" -#include "core/hle/service/apm/interface.h" - -namespace Service::APM { - -class ISession final : public ServiceFramework<ISession> { -public: - explicit ISession(Core::System& system_, Controller& controller_) - : ServiceFramework{system_, "ISession"}, controller{controller_} { - static const FunctionInfo functions[] = { - {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, - {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, - {2, nullptr, "SetCpuOverclockEnabled"}, - }; - RegisterHandlers(functions); - } - -private: - void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto mode = rp.PopEnum<PerformanceMode>(); - const auto config = rp.PopEnum<PerformanceConfiguration>(); - LOG_DEBUG(Service_APM, "called mode={} config={}", mode, config); - - controller.SetPerformanceConfiguration(mode, config); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - const auto mode = rp.PopEnum<PerformanceMode>(); - LOG_DEBUG(Service_APM, "called mode={}", mode); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(controller.GetCurrentPerformanceConfiguration(mode)); - } - - Controller& controller; -}; - -APM::APM(Core::System& system_, std::shared_ptr<Module> apm_, Controller& controller_, - const char* name) - : ServiceFramework{system_, name}, apm(std::move(apm_)), controller{controller_} { - static const FunctionInfo functions[] = { - {0, &APM::OpenSession, "OpenSession"}, - {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, - {6, &APM::IsCpuOverclockEnabled, "IsCpuOverclockEnabled"}, - }; - RegisterHandlers(functions); -} - -APM::~APM() = default; - -void APM::OpenSession(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_APM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISession>(system, controller); -} - -void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_APM, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.PushEnum(controller.GetCurrentPerformanceMode()); -} - -void APM::IsCpuOverclockEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_APM, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); -} - -APM_Sys::APM_Sys(Core::System& system_, Controller& controller_) - : ServiceFramework{system_, "apm:sys"}, controller{controller_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "RequestPerformanceMode"}, - {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"}, - {2, nullptr, "GetThrottlingState"}, - {3, nullptr, "GetLastThrottlingState"}, - {4, nullptr, "ClearLastThrottlingState"}, - {5, nullptr, "LoadAndApplySettings"}, - {6, &APM_Sys::SetCpuBoostMode, "SetCpuBoostMode"}, - {7, &APM_Sys::GetCurrentPerformanceConfiguration, "GetCurrentPerformanceConfiguration"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -APM_Sys::~APM_Sys() = default; - -void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_APM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<ISession>(system, controller); -} - -void APM_Sys::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto mode = rp.PopEnum<CpuBoostMode>(); - - LOG_DEBUG(Service_APM, "called, mode={:08X}", mode); - - controller.SetFromCpuBoostMode(mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void APM_Sys::GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_APM, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum( - controller.GetCurrentPerformanceConfiguration(controller.GetCurrentPerformanceMode())); -} - -} // namespace Service::APM diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index dc15cf58b..7ca7f2aac 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp @@ -7,6 +7,9 @@ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshadow" +#ifndef __clang__ +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif #endif #include <httplib.h> #include <mbedtls/sha256.h> diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h index d72798980..1eba477da 100644 --- a/src/core/hle/service/bcat/bcat.h +++ b/src/core/hle/service/bcat/bcat.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/bcat/module.h" +#include "core/hle/service/bcat/bcat_module.h" namespace Core { class System; diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp new file mode 100644 index 000000000..72294eb2e --- /dev/null +++ b/src/core/hle/service/bcat/bcat_module.cpp @@ -0,0 +1,610 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cctype> +#include <mbedtls/md5.h> +#include "backend/boxcat.h" +#include "common/hex_util.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "common/string_util.h" +#include "core/core.h" +#include "core/file_sys/vfs.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/bcat/backend/backend.h" +#include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bcat/bcat_module.h" +#include "core/hle/service/filesystem/filesystem.h" + +namespace Service::BCAT { + +constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; +constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; +constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; +constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; + +// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files +// and if any of them have a non-zero result it just forwards that result. This is the FS error code +// for permission denied, which is the closest approximation of this scenario. +constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; + +using BCATDigest = std::array<u8, 0x10>; + +namespace { + +u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { + u64 out{}; + std::memcpy(&out, id.data(), sizeof(u64)); + return out; +} + +// The digest is only used to determine if a file is unique compared to others of the same name. +// Since the algorithm isn't ever checked in game, MD5 is safe. +BCATDigest DigestFile(const FileSys::VirtualFile& file) { + BCATDigest out{}; + const auto bytes = file->ReadAllBytes(); + mbedtls_md5_ret(bytes.data(), bytes.size(), out.data()); + return out; +} + +// For a name to be valid it must be non-empty, must have a null terminating character as the final +// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if +// file. +bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name, + char match_char) { + const auto null_chars = std::count(name.begin(), name.end(), 0); + const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { + return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; + }); + if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { + LOG_ERROR(Service_BCAT, "Name passed was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return false; + } + + return true; +} + +bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) { + return VerifyNameValidInternal(ctx, name, '-'); +} + +bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) { + return VerifyNameValidInternal(ctx, name, '.'); +} + +} // Anonymous namespace + +struct DeliveryCacheDirectoryEntry { + FileName name; + u64 size; + BCATDigest digest; +}; + +class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { +public: + explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_, + const DeliveryCacheProgressImpl& impl_) + : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, + {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(event); + } + + void GetImpl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + ctx.WriteBuffer(impl); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + Kernel::KReadableEvent& event; + const DeliveryCacheProgressImpl& impl; +}; + +class IBcatService final : public ServiceFramework<IBcatService> { +public: + explicit IBcatService(Core::System& system_, Backend& backend_) + : ServiceFramework{system_, "IBcatService"}, backend{backend_}, + progress{{ + ProgressServiceBackend{system_.Kernel(), "Normal"}, + ProgressServiceBackend{system_.Kernel(), "Directory"}, + }} { + // clang-format off + static const FunctionInfo functions[] = { + {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"}, + {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"}, + {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, + {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, + {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, + {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"}, + {20301, nullptr, "RequestSuspendDeliveryTask"}, + {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, + {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, + {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, + {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, + {30101, nullptr, "Unknown"}, + {30102, nullptr, "Unknown2"}, + {30200, nullptr, "RegisterBackgroundDeliveryTask"}, + {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, + {30202, nullptr, "BlockDeliveryTask"}, + {30203, nullptr, "UnblockDeliveryTask"}, + {30210, nullptr, "SetDeliveryTaskTimer"}, + {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, + {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, + {90101, nullptr, "Unknown90101"}, + {90200, nullptr, "GetDeliveryList"}, + {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, + {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"}, + {90300, nullptr, "GetPushNotificationLog"}, + {90301, nullptr, "Unknown90301"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + enum class SyncType { + Normal, + Directory, + Count, + }; + + std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { + auto& progress_backend{GetProgressBackend(type)}; + return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(), + progress_backend.GetImpl()); + } + + void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + backend.Synchronize({system.CurrentProcess()->GetTitleID(), + GetCurrentBuildID(system.GetCurrentProcessBuildID())}, + GetProgressBackend(SyncType::Normal)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(CreateProgressService(SyncType::Normal)); + } + + void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto name_raw = rp.PopRaw<DirectoryName>(); + const auto name = + Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, name={}", name); + + backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(), + GetCurrentBuildID(system.GetCurrentProcessBuildID())}, + name, GetProgressBackend(SyncType::Directory)); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); + } + + void SetPassphrase(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + const auto passphrase_raw = ctx.ReadBuffer(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, + Common::HexToString(passphrase_raw)); + + if (title_id == 0) { + LOG_ERROR(Service_BCAT, "Invalid title ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + } + + if (passphrase_raw.size() > 0x40) { + LOG_ERROR(Service_BCAT, "Passphrase too large!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + Passphrase passphrase{}; + std::memcpy(passphrase.data(), passphrase_raw.data(), + std::min(passphrase.size(), passphrase_raw.size())); + + backend.SetPassphrase(title_id, passphrase); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); + + if (title_id == 0) { + LOG_ERROR(Service_BCAT, "Invalid title ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + if (!backend.Clear(title_id)) { + LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_CLEAR_CACHE); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + ProgressServiceBackend& GetProgressBackend(SyncType type) { + return progress.at(static_cast<size_t>(type)); + } + + const ProgressServiceBackend& GetProgressBackend(SyncType type) const { + return progress.at(static_cast<size_t>(type)); + } + + Backend& backend; + std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress; +}; + +void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IBcatService>(system, *backend); +} + +class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> { +public: + explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_) + : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheFileService::Open, "Open"}, + {1, &IDeliveryCacheFileService::Read, "Read"}, + {2, &IDeliveryCacheFileService::GetSize, "GetSize"}, + {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Open(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto dir_name_raw = rp.PopRaw<DirectoryName>(); + const auto file_name_raw = rp.PopRaw<FileName>(); + + const auto dir_name = + Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); + const auto file_name = + Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); + + if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw)) + return; + + if (current_file != nullptr) { + LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ENTITY_ALREADY_OPEN); + return; + } + + const auto dir = root->GetSubdirectory(dir_name); + + if (dir == nullptr) { + LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + current_file = dir->GetFile(file_name); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto offset{rp.PopRaw<u64>()}; + + auto size = ctx.GetWriteBufferSize(); + + LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + size = std::min<u64>(current_file->GetSize() - offset, size); + const auto buffer = current_file->ReadBytes(size, offset); + ctx.WriteBuffer(buffer); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(buffer.size()); + } + + void GetSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push<u64>(current_file->GetSize()); + } + + void GetDigest(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_file == nullptr) { + LOG_ERROR(Service_BCAT, "There is no file currently open!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(DigestFile(current_file)); + } + + FileSys::VirtualDir root; + FileSys::VirtualFile current_file; +}; + +class IDeliveryCacheDirectoryService final + : public ServiceFramework<IDeliveryCacheDirectoryService> { +public: + explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_) + : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheDirectoryService::Open, "Open"}, + {1, &IDeliveryCacheDirectoryService::Read, "Read"}, + {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Open(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto name_raw = rp.PopRaw<DirectoryName>(); + const auto name = + Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); + + LOG_DEBUG(Service_BCAT, "called, name={}", name); + + if (!VerifyNameValidDir(ctx, name_raw)) + return; + + if (current_dir != nullptr) { + LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_ENTITY_ALREADY_OPEN); + return; + } + + current_dir = root->GetSubdirectory(name); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_FAILED_OPEN_ENTITY); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void Read(Kernel::HLERequestContext& ctx) { + auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); + + LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "There is no open directory!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + return; + } + + const auto files = current_dir->GetFiles(); + write_size = std::min<u64>(write_size, files.size()); + std::vector<DeliveryCacheDirectoryEntry> entries(write_size); + std::transform( + files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { + FileName name{}; + std::memcpy(name.data(), file->GetName().data(), + std::min(file->GetName().size(), name.size())); + return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; + }); + + ctx.WriteBuffer(entries); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry))); + } + + void GetCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + if (current_dir == nullptr) { + LOG_ERROR(Service_BCAT, "There is no open directory!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_NO_OPEN_ENTITY); + return; + } + + const auto files = current_dir->GetFiles(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(files.size())); + } + + FileSys::VirtualDir root; + FileSys::VirtualDir current_dir; +}; + +class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> { +public: + explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_) + : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"}, + {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"}, + {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"}, + }; + // clang-format on + + RegisterHandlers(functions); + + for (const auto& subdir : root->GetSubdirectories()) { + DirectoryName name{}; + std::memcpy(name.data(), subdir->GetName().data(), + std::min(sizeof(DirectoryName) - 1, subdir->GetName().size())); + entries.push_back(name); + } + } + +private: + void CreateFileService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDeliveryCacheFileService>(system, root); + } + + void CreateDirectoryService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root); + } + + void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { + auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); + + LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); + + size = std::min<u64>(size, entries.size() - next_read_index); + ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); + next_read_index += size; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(size)); + } + + FileSys::VirtualDir root; + std::vector<DirectoryName> entries; + u64 next_read_index = 0; +}; + +void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_BCAT, "called"); + + const auto title_id = system.CurrentProcess()->GetTitleID(); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); +} + +void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); +} + +std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, + DirectoryGetter getter) { +#ifdef YUZU_ENABLE_BOXCAT + if (Settings::values.bcat_backend.GetValue() == "boxcat") { + return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter)); + } +#endif + + return std::make_unique<NullBackend>(std::move(getter)); +} + +Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, + FileSystem::FileSystemController& fsc_, const char* name) + : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)}, + backend{CreateBackendFromSettings(system_, + [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {} + +Module::Interface::~Interface() = default; + +void InstallInterfaces(Core::System& system) { + auto module = std::make_shared<Module>(); + std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a") + ->InstallAsService(system.ServiceManager()); + std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m") + ->InstallAsService(system.ServiceManager()); + std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u") + ->InstallAsService(system.ServiceManager()); + std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s") + ->InstallAsService(system.ServiceManager()); +} + +} // namespace Service::BCAT diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/bcat_module.h index 738731c06..738731c06 100644 --- a/src/core/hle/service/bcat/module.h +++ b/src/core/hle/service/bcat/bcat_module.h diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp deleted file mode 100644 index f85444da8..000000000 --- a/src/core/hle/service/bcat/module.cpp +++ /dev/null @@ -1,610 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cctype> -#include <mbedtls/md5.h> -#include "backend/boxcat.h" -#include "common/hex_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/string_util.h" -#include "core/core.h" -#include "core/file_sys/vfs.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_writable_event.h" -#include "core/hle/service/bcat/backend/backend.h" -#include "core/hle/service/bcat/bcat.h" -#include "core/hle/service/bcat/module.h" -#include "core/hle/service/filesystem/filesystem.h" - -namespace Service::BCAT { - -constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; -constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; -constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; -constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; - -// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files -// and if any of them have a non-zero result it just forwards that result. This is the FS error code -// for permission denied, which is the closest approximation of this scenario. -constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; - -using BCATDigest = std::array<u8, 0x10>; - -namespace { - -u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { - u64 out{}; - std::memcpy(&out, id.data(), sizeof(u64)); - return out; -} - -// The digest is only used to determine if a file is unique compared to others of the same name. -// Since the algorithm isn't ever checked in game, MD5 is safe. -BCATDigest DigestFile(const FileSys::VirtualFile& file) { - BCATDigest out{}; - const auto bytes = file->ReadAllBytes(); - mbedtls_md5_ret(bytes.data(), bytes.size(), out.data()); - return out; -} - -// For a name to be valid it must be non-empty, must have a null terminating character as the final -// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if -// file. -bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name, - char match_char) { - const auto null_chars = std::count(name.begin(), name.end(), 0); - const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { - return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; - }); - if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { - LOG_ERROR(Service_BCAT, "Name passed was invalid!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - return false; - } - - return true; -} - -bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) { - return VerifyNameValidInternal(ctx, name, '-'); -} - -bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) { - return VerifyNameValidInternal(ctx, name, '.'); -} - -} // Anonymous namespace - -struct DeliveryCacheDirectoryEntry { - FileName name; - u64 size; - BCATDigest digest; -}; - -class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { -public: - explicit IDeliveryCacheProgressService(Core::System& system_, Kernel::KReadableEvent& event_, - const DeliveryCacheProgressImpl& impl_) - : ServiceFramework{system_, "IDeliveryCacheProgressService"}, event{event_}, impl{impl_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, - {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void GetEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(event); - } - - void GetImpl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - ctx.WriteBuffer(impl); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - Kernel::KReadableEvent& event; - const DeliveryCacheProgressImpl& impl; -}; - -class IBcatService final : public ServiceFramework<IBcatService> { -public: - explicit IBcatService(Core::System& system_, Backend& backend_) - : ServiceFramework{system_, "IBcatService"}, backend{backend_}, - progress{{ - ProgressServiceBackend{system_.Kernel(), "Normal"}, - ProgressServiceBackend{system_.Kernel(), "Directory"}, - }} { - // clang-format off - static const FunctionInfo functions[] = { - {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"}, - {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"}, - {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, - {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, - {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, - {20300, nullptr, "GetDeliveryCacheStorageUpdateNotifier"}, - {20301, nullptr, "RequestSuspendDeliveryTask"}, - {20400, nullptr, "RegisterSystemApplicationDeliveryTask"}, - {20401, nullptr, "UnregisterSystemApplicationDeliveryTask"}, - {20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"}, - {30100, &IBcatService::SetPassphrase, "SetPassphrase"}, - {30101, nullptr, "Unknown"}, - {30102, nullptr, "Unknown2"}, - {30200, nullptr, "RegisterBackgroundDeliveryTask"}, - {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, - {30202, nullptr, "BlockDeliveryTask"}, - {30203, nullptr, "UnblockDeliveryTask"}, - {30210, nullptr, "SetDeliveryTaskTimer"}, - {30300, nullptr, "RegisterSystemApplicationDeliveryTasks"}, - {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, - {90101, nullptr, "Unknown90101"}, - {90200, nullptr, "GetDeliveryList"}, - {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"}, - {90202, nullptr, "ClearDeliveryTaskSubscriptionStatus"}, - {90300, nullptr, "GetPushNotificationLog"}, - {90301, nullptr, "Unknown90301"}, - }; - // clang-format on - RegisterHandlers(functions); - } - -private: - enum class SyncType { - Normal, - Directory, - Count, - }; - - std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { - auto& progress_backend{GetProgressBackend(type)}; - return std::make_shared<IDeliveryCacheProgressService>(system, progress_backend.GetEvent(), - progress_backend.GetImpl()); - } - - void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - backend.Synchronize({system.CurrentProcess()->GetTitleID(), - GetCurrentBuildID(system.GetCurrentProcessBuildID())}, - GetProgressBackend(SyncType::Normal)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(CreateProgressService(SyncType::Normal)); - } - - void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto name_raw = rp.PopRaw<DirectoryName>(); - const auto name = - Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); - - LOG_DEBUG(Service_BCAT, "called, name={}", name); - - backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(), - GetCurrentBuildID(system.GetCurrentProcessBuildID())}, - name, GetProgressBackend(SyncType::Directory)); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(CreateProgressService(SyncType::Directory)); - } - - void SetPassphrase(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto title_id = rp.PopRaw<u64>(); - - const auto passphrase_raw = ctx.ReadBuffer(); - - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, - Common::HexToString(passphrase_raw)); - - if (title_id == 0) { - LOG_ERROR(Service_BCAT, "Invalid title ID!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - } - - if (passphrase_raw.size() > 0x40) { - LOG_ERROR(Service_BCAT, "Passphrase too large!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - return; - } - - Passphrase passphrase{}; - std::memcpy(passphrase.data(), passphrase_raw.data(), - std::min(passphrase.size(), passphrase_raw.size())); - - backend.SetPassphrase(title_id, passphrase); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto title_id = rp.PopRaw<u64>(); - - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); - - if (title_id == 0) { - LOG_ERROR(Service_BCAT, "Invalid title ID!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_INVALID_ARGUMENT); - return; - } - - if (!backend.Clear(title_id)) { - LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_FAILED_CLEAR_CACHE); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - ProgressServiceBackend& GetProgressBackend(SyncType type) { - return progress.at(static_cast<size_t>(type)); - } - - const ProgressServiceBackend& GetProgressBackend(SyncType type) const { - return progress.at(static_cast<size_t>(type)); - } - - Backend& backend; - std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress; -}; - -void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IBcatService>(system, *backend); -} - -class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> { -public: - explicit IDeliveryCacheFileService(Core::System& system_, FileSys::VirtualDir root_) - : ServiceFramework{system_, "IDeliveryCacheFileService"}, root(std::move(root_)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IDeliveryCacheFileService::Open, "Open"}, - {1, &IDeliveryCacheFileService::Read, "Read"}, - {2, &IDeliveryCacheFileService::GetSize, "GetSize"}, - {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void Open(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto dir_name_raw = rp.PopRaw<DirectoryName>(); - const auto file_name_raw = rp.PopRaw<FileName>(); - - const auto dir_name = - Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size()); - const auto file_name = - Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size()); - - LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name); - - if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw)) - return; - - if (current_file != nullptr) { - LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_ENTITY_ALREADY_OPEN); - return; - } - - const auto dir = root->GetSubdirectory(dir_name); - - if (dir == nullptr) { - LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_FAILED_OPEN_ENTITY); - return; - } - - current_file = dir->GetFile(file_name); - - if (current_file == nullptr) { - LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_FAILED_OPEN_ENTITY); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Read(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto offset{rp.PopRaw<u64>()}; - - auto size = ctx.GetWriteBufferSize(); - - LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size); - - if (current_file == nullptr) { - LOG_ERROR(Service_BCAT, "There is no file currently open!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_NO_OPEN_ENTITY); - } - - size = std::min<u64>(current_file->GetSize() - offset, size); - const auto buffer = current_file->ReadBytes(size, offset); - ctx.WriteBuffer(buffer); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(buffer.size()); - } - - void GetSize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - if (current_file == nullptr) { - LOG_ERROR(Service_BCAT, "There is no file currently open!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_NO_OPEN_ENTITY); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push<u64>(current_file->GetSize()); - } - - void GetDigest(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - if (current_file == nullptr) { - LOG_ERROR(Service_BCAT, "There is no file currently open!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_NO_OPEN_ENTITY); - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(DigestFile(current_file)); - } - - FileSys::VirtualDir root; - FileSys::VirtualFile current_file; -}; - -class IDeliveryCacheDirectoryService final - : public ServiceFramework<IDeliveryCacheDirectoryService> { -public: - explicit IDeliveryCacheDirectoryService(Core::System& system_, FileSys::VirtualDir root_) - : ServiceFramework{system_, "IDeliveryCacheDirectoryService"}, root(std::move(root_)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IDeliveryCacheDirectoryService::Open, "Open"}, - {1, &IDeliveryCacheDirectoryService::Read, "Read"}, - {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void Open(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto name_raw = rp.PopRaw<DirectoryName>(); - const auto name = - Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size()); - - LOG_DEBUG(Service_BCAT, "called, name={}", name); - - if (!VerifyNameValidDir(ctx, name_raw)) - return; - - if (current_dir != nullptr) { - LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_ENTITY_ALREADY_OPEN); - return; - } - - current_dir = root->GetSubdirectory(name); - - if (current_dir == nullptr) { - LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_FAILED_OPEN_ENTITY); - return; - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void Read(Kernel::HLERequestContext& ctx) { - auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry); - - LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size); - - if (current_dir == nullptr) { - LOG_ERROR(Service_BCAT, "There is no open directory!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_NO_OPEN_ENTITY); - return; - } - - const auto files = current_dir->GetFiles(); - write_size = std::min<u64>(write_size, files.size()); - std::vector<DeliveryCacheDirectoryEntry> entries(write_size); - std::transform( - files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) { - FileName name{}; - std::memcpy(name.data(), file->GetName().data(), - std::min(file->GetName().size(), name.size())); - return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)}; - }); - - ctx.WriteBuffer(entries); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry))); - } - - void GetCount(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - if (current_dir == nullptr) { - LOG_ERROR(Service_BCAT, "There is no open directory!"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ERROR_NO_OPEN_ENTITY); - return; - } - - const auto files = current_dir->GetFiles(); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(files.size())); - } - - FileSys::VirtualDir root; - FileSys::VirtualDir current_dir; -}; - -class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> { -public: - explicit IDeliveryCacheStorageService(Core::System& system_, FileSys::VirtualDir root_) - : ServiceFramework{system_, "IDeliveryCacheStorageService"}, root(std::move(root_)) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"}, - {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"}, - {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"}, - }; - // clang-format on - - RegisterHandlers(functions); - - for (const auto& subdir : root->GetSubdirectories()) { - DirectoryName name{}; - std::memcpy(name.data(), subdir->GetName().data(), - std::min(sizeof(DirectoryName) - 1, subdir->GetName().size())); - entries.push_back(name); - } - } - -private: - void CreateFileService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDeliveryCacheFileService>(system, root); - } - - void CreateDirectoryService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDeliveryCacheDirectoryService>(system, root); - } - - void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) { - auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName); - - LOG_DEBUG(Service_BCAT, "called, size={:016X}", size); - - size = std::min<u64>(size, entries.size() - next_read_index); - ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName)); - next_read_index += size; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(size)); - } - - FileSys::VirtualDir root; - std::vector<DirectoryName> entries; - u64 next_read_index = 0; -}; - -void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_BCAT, "called"); - - const auto title_id = system.CurrentProcess()->GetTitleID(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); -} - -void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId( - Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto title_id = rp.PopRaw<u64>(); - - LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id)); -} - -std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system, - DirectoryGetter getter) { -#ifdef YUZU_ENABLE_BOXCAT - if (Settings::values.bcat_backend.GetValue() == "boxcat") { - return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter)); - } -#endif - - return std::make_unique<NullBackend>(std::move(getter)); -} - -Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, - FileSystem::FileSystemController& fsc_, const char* name) - : ServiceFramework{system_, name}, fsc{fsc_}, module{std::move(module_)}, - backend{CreateBackendFromSettings(system_, - [&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })} {} - -Module::Interface::~Interface() = default; - -void InstallInterfaces(Core::System& system) { - auto module = std::make_shared<Module>(); - std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a") - ->InstallAsService(system.ServiceManager()); - std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m") - ->InstallAsService(system.ServiceManager()); - std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u") - ->InstallAsService(system.ServiceManager()); - std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s") - ->InstallAsService(system.ServiceManager()); -} - -} // namespace Service::BCAT diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index c5f88bce7..a3c939c0c 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -12,7 +12,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/service/friend/errors.h" #include "core/hle/service/friend/friend.h" -#include "core/hle/service/friend/interface.h" +#include "core/hle/service/friend/friend_interface.h" namespace Service::Friend { diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp new file mode 100644 index 000000000..9b18b2a32 --- /dev/null +++ b/src/core/hle/service/friend/friend_interface.cpp @@ -0,0 +1,21 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/friend/friend_interface.h" + +namespace Service::Friend { + +Friend::Friend(std::shared_ptr<Module> module_, Core::System& system_, const char* name) + : Interface(std::move(module_), system_, name) { + static const FunctionInfo functions[] = { + {0, &Friend::CreateFriendService, "CreateFriendService"}, + {1, &Friend::CreateNotificationService, "CreateNotificationService"}, + {2, nullptr, "CreateDaemonSuspendSessionService"}, + }; + RegisterHandlers(functions); +} + +Friend::~Friend() = default; + +} // namespace Service::Friend diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/friend_interface.h index 43d914b32..43d914b32 100644 --- a/src/core/hle/service/friend/interface.h +++ b/src/core/hle/service/friend/friend_interface.h diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp deleted file mode 100644 index 7368ccec2..000000000 --- a/src/core/hle/service/friend/interface.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/friend/interface.h" - -namespace Service::Friend { - -Friend::Friend(std::shared_ptr<Module> module_, Core::System& system_, const char* name) - : Interface(std::move(module_), system_, name) { - static const FunctionInfo functions[] = { - {0, &Friend::CreateFriendService, "CreateFriendService"}, - {1, &Friend::CreateNotificationService, "CreateNotificationService"}, - {2, nullptr, "CreateDaemonSuspendSessionService"}, - }; - RegisterHandlers(functions); -} - -Friend::~Friend() = default; - -} // namespace Service::Friend diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp index ca25df67e..5a3b54cc1 100644 --- a/src/core/hle/service/glue/arp.cpp +++ b/src/core/hle/service/glue/arp.cpp @@ -13,7 +13,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/service/glue/arp.h" #include "core/hle/service/glue/errors.h" -#include "core/hle/service/glue/manager.h" +#include "core/hle/service/glue/glue_manager.h" #include "core/hle/service/service.h" namespace Service::Glue { diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp new file mode 100644 index 000000000..aa9d48c0c --- /dev/null +++ b/src/core/hle/service/glue/glue_manager.cpp @@ -0,0 +1,78 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/glue/errors.h" +#include "core/hle/service/glue/glue_manager.h" + +namespace Service::Glue { + +struct ARPManager::MapEntry { + ApplicationLaunchProperty launch; + std::vector<u8> control; +}; + +ARPManager::ARPManager() = default; + +ARPManager::~ARPManager() = default; + +ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const { + if (title_id == 0) { + return ERR_INVALID_PROCESS_ID; + } + + const auto iter = entries.find(title_id); + if (iter == entries.end()) { + return ERR_NOT_REGISTERED; + } + + return MakeResult<ApplicationLaunchProperty>(iter->second.launch); +} + +ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const { + if (title_id == 0) { + return ERR_INVALID_PROCESS_ID; + } + + const auto iter = entries.find(title_id); + if (iter == entries.end()) { + return ERR_NOT_REGISTERED; + } + + return MakeResult<std::vector<u8>>(iter->second.control); +} + +ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, + std::vector<u8> control) { + if (title_id == 0) { + return ERR_INVALID_PROCESS_ID; + } + + const auto iter = entries.find(title_id); + if (iter != entries.end()) { + return ERR_INVALID_ACCESS; + } + + entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)}); + return ResultSuccess; +} + +ResultCode ARPManager::Unregister(u64 title_id) { + if (title_id == 0) { + return ERR_INVALID_PROCESS_ID; + } + + const auto iter = entries.find(title_id); + if (iter == entries.end()) { + return ERR_NOT_REGISTERED; + } + + entries.erase(iter); + return ResultSuccess; +} + +void ARPManager::ResetAll() { + entries.clear(); +} + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/manager.h b/src/core/hle/service/glue/glue_manager.h index a7f5ce3ee..a7f5ce3ee 100644 --- a/src/core/hle/service/glue/manager.h +++ b/src/core/hle/service/glue/glue_manager.h diff --git a/src/core/hle/service/glue/manager.cpp b/src/core/hle/service/glue/manager.cpp deleted file mode 100644 index 9b1754cf8..000000000 --- a/src/core/hle/service/glue/manager.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/glue/errors.h" -#include "core/hle/service/glue/manager.h" - -namespace Service::Glue { - -struct ARPManager::MapEntry { - ApplicationLaunchProperty launch; - std::vector<u8> control; -}; - -ARPManager::ARPManager() = default; - -ARPManager::~ARPManager() = default; - -ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const { - if (title_id == 0) { - return ERR_INVALID_PROCESS_ID; - } - - const auto iter = entries.find(title_id); - if (iter == entries.end()) { - return ERR_NOT_REGISTERED; - } - - return MakeResult<ApplicationLaunchProperty>(iter->second.launch); -} - -ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const { - if (title_id == 0) { - return ERR_INVALID_PROCESS_ID; - } - - const auto iter = entries.find(title_id); - if (iter == entries.end()) { - return ERR_NOT_REGISTERED; - } - - return MakeResult<std::vector<u8>>(iter->second.control); -} - -ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch, - std::vector<u8> control) { - if (title_id == 0) { - return ERR_INVALID_PROCESS_ID; - } - - const auto iter = entries.find(title_id); - if (iter != entries.end()) { - return ERR_INVALID_ACCESS; - } - - entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)}); - return ResultSuccess; -} - -ResultCode ARPManager::Unregister(u64 title_id) { - if (title_id == 0) { - return ERR_INVALID_PROCESS_ID; - } - - const auto iter = entries.find(title_id); - if (iter == entries.end()) { - return ERR_NOT_REGISTERED; - } - - entries.erase(iter); - return ResultSuccess; -} - -void ARPManager::ResetAll() { - entries.clear(); -} - -} // namespace Service::Glue diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 1eb02aee2..b7f551e40 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -18,6 +18,7 @@ #include "core/hle/kernel/k_writable_event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/kernel_helpers.h" namespace Service::HID { constexpr s32 HID_JOYSTICK_MAX = 0x7fff; @@ -147,7 +148,9 @@ bool Controller_NPad::IsDeviceHandleValid(const DeviceHandle& device_handle) { device_handle.device_index < DeviceIndex::MaxDeviceIndex; } -Controller_NPad::Controller_NPad(Core::System& system_) : ControllerBase{system_} { +Controller_NPad::Controller_NPad(Core::System& system_, + KernelHelpers::ServiceContext& service_context_) + : ControllerBase{system_}, service_context{service_context_} { latest_vibration_values.fill({DEFAULT_VIBRATION_VALUE, DEFAULT_VIBRATION_VALUE}); } @@ -251,10 +254,9 @@ void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { } void Controller_NPad::OnInit() { - auto& kernel = system.Kernel(); for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { - styleset_changed_events[i] = Kernel::KEvent::Create(kernel); - styleset_changed_events[i]->Initialize(fmt::format("npad:NpadStyleSetChanged_{}", i)); + styleset_changed_events[i] = + service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); } if (!IsControllerActivated()) { @@ -344,8 +346,7 @@ void Controller_NPad::OnRelease() { } for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) { - styleset_changed_events[i]->Close(); - styleset_changed_events[i] = nullptr; + service_context.CloseEvent(styleset_changed_events[i]); } } @@ -941,6 +942,11 @@ void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_de void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index) { + if (!Settings::values.vibration_enabled.GetValue()) { + vibration_devices_mounted[npad_index][device_index] = false; + return; + } + if (vibrations[npad_index][device_index]) { vibration_devices_mounted[npad_index][device_index] = vibrations[npad_index][device_index]->GetStatus() == 1; diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 1409d82a2..4fcc6f93a 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -20,6 +20,10 @@ class KEvent; class KReadableEvent; } // namespace Kernel +namespace Service::KernelHelpers { +class ServiceContext; +} + namespace Service::HID { constexpr u32 NPAD_HANDHELD = 32; @@ -27,7 +31,8 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? class Controller_NPad final : public ControllerBase { public: - explicit Controller_NPad(Core::System& system_); + explicit Controller_NPad(Core::System& system_, + KernelHelpers::ServiceContext& service_context_); ~Controller_NPad() override; // Called when the controller is initialized @@ -566,6 +571,7 @@ private: std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>, 10>; + KernelHelpers::ServiceContext& service_context; std::mutex mutex; ButtonArray buttons; StickArray sticks; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d68b023d0..b8b80570d 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -46,8 +46,9 @@ constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // 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_) - : ServiceFramework{system_, "IAppletResource"} { +IAppletResource::IAppletResource(Core::System& system_, + KernelHelpers::ServiceContext& service_context_) + : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} { static const FunctionInfo functions[] = { {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; @@ -63,7 +64,7 @@ IAppletResource::IAppletResource(Core::System& system_) MakeController<Controller_Stubbed>(HidController::CaptureButton); MakeController<Controller_Stubbed>(HidController::InputDetector); MakeController<Controller_Stubbed>(HidController::UniquePad); - MakeController<Controller_NPad>(HidController::NPad); + MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad); MakeController<Controller_Gesture>(HidController::Gesture); MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor); @@ -191,13 +192,14 @@ private: std::shared_ptr<IAppletResource> Hid::GetAppletResource() { if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system); + applet_resource = std::make_shared<IAppletResource>(system, service_context); } return applet_resource; } -Hid::Hid(Core::System& system_) : ServiceFramework{system_, "hid"} { +Hid::Hid(Core::System& system_) + : ServiceFramework{system_, "hid"}, service_context{system_, service_name} { // clang-format off static const FunctionInfo functions[] = { {0, &Hid::CreateAppletResource, "CreateAppletResource"}, @@ -347,7 +349,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(system); + applet_resource = std::make_shared<IAppletResource>(system, service_context); } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 83fc2ea1d..9c5c7f252 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -7,6 +7,7 @@ #include <chrono> #include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/service.h" namespace Core::Timing { @@ -39,7 +40,8 @@ enum class HidController : std::size_t { class IAppletResource final : public ServiceFramework<IAppletResource> { public: - explicit IAppletResource(Core::System& system_); + explicit IAppletResource(Core::System& system_, + KernelHelpers::ServiceContext& service_context_); ~IAppletResource() override; void ActivateController(HidController controller); @@ -60,11 +62,18 @@ private: void MakeController(HidController controller) { controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); } + template <typename T> + void MakeControllerWithServiceContext(HidController controller) { + controllers[static_cast<std::size_t>(controller)] = + std::make_unique<T>(system, service_context); + } 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); + KernelHelpers::ServiceContext& service_context; + std::shared_ptr<Core::Timing::EventType> pad_update_event; std::shared_ptr<Core::Timing::EventType> motion_update_event; @@ -176,6 +185,8 @@ private: static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size."); std::shared_ptr<IAppletResource> applet_resource; + + KernelHelpers::ServiceContext service_context; }; /// Reload input devices. Used when input configuration changed diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp new file mode 100644 index 000000000..62f4cdfb2 --- /dev/null +++ b/src/core/hle/service/kernel_helpers.cpp @@ -0,0 +1,62 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_resource_limit.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/service/kernel_helpers.h" + +namespace Service::KernelHelpers { + +ServiceContext::ServiceContext(Core::System& system_, std::string name_) + : kernel(system_.Kernel()) { + process = Kernel::KProcess::Create(kernel); + ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), + Kernel::KProcess::ProcessType::Userland) + .IsSuccess()); +} + +ServiceContext::~ServiceContext() { + process->Close(); + process = nullptr; +} + +Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { + // Reserve a new event from the process resource limit + Kernel::KScopedResourceReservation event_reservation(process, + Kernel::LimitableResource::Events); + if (!event_reservation.Succeeded()) { + LOG_CRITICAL(Service, "Resource limit reached!"); + return {}; + } + + // Create a new event. + auto* event = Kernel::KEvent::Create(kernel); + if (!event) { + LOG_CRITICAL(Service, "Unable to create event!"); + return {}; + } + + // Initialize the event. + event->Initialize(std::move(name)); + + // Commit the thread reservation. + event_reservation.Commit(); + + // Register the event. + Kernel::KEvent::Register(kernel, event); + + return event; +} + +void ServiceContext::CloseEvent(Kernel::KEvent* event) { + event->GetReadableEvent().Close(); + event->GetWritableEvent().Close(); +} + +} // namespace Service::KernelHelpers diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h new file mode 100644 index 000000000..4f3e95f67 --- /dev/null +++ b/src/core/hle/service/kernel_helpers.h @@ -0,0 +1,35 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> + +namespace Core { +class System; +} + +namespace Kernel { +class KernelCore; +class KEvent; +class KProcess; +} // namespace Kernel + +namespace Service::KernelHelpers { + +class ServiceContext { +public: + ServiceContext(Core::System& system_, std::string name_); + ~ServiceContext(); + + Kernel::KEvent* CreateEvent(std::string&& name); + + void CloseEvent(Kernel::KEvent* event); + +private: + Kernel::KernelCore& kernel; + Kernel::KProcess* process{}; +}; + +} // namespace Service::KernelHelpers diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp deleted file mode 100644 index 869d2763f..000000000 --- a/src/core/hle/service/mii/manager.cpp +++ /dev/null @@ -1,465 +0,0 @@ -// Copyright 2020 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> -#include <random> - -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/string_util.h" - -#include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/mii/manager.h" -#include "core/hle/service/mii/raw_data.h" -#include "core/hle/service/mii/types.h" - -namespace Service::Mii { - -namespace { - -constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; - -constexpr std::size_t BaseMiiCount{2}; -constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; - -constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; -constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7}; -constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13}; -constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23}; -constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0}; -constexpr std::array<u8, 62> EyeRotateLookup{ - {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, - 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, - 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}}; -constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, - 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, - 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}}; - -template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> -std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { - std::array<T, DestArraySize> out{}; - std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); - return out; -} - -MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { - MiiStoreBitFields bf; - std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); - - return { - .uuid = data.data.uuid, - .name = ResizeArray<char16_t, 10, 11>(data.data.name), - .font_region = static_cast<u8>(bf.font_region.Value()), - .favorite_color = static_cast<u8>(bf.favorite_color.Value()), - .gender = static_cast<u8>(bf.gender.Value()), - .height = static_cast<u8>(bf.height.Value()), - .build = static_cast<u8>(bf.build.Value()), - .type = static_cast<u8>(bf.type.Value()), - .region_move = static_cast<u8>(bf.region_move.Value()), - .faceline_type = static_cast<u8>(bf.faceline_type.Value()), - .faceline_color = static_cast<u8>(bf.faceline_color.Value()), - .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()), - .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()), - .hair_type = static_cast<u8>(bf.hair_type.Value()), - .hair_color = static_cast<u8>(bf.hair_color.Value()), - .hair_flip = static_cast<u8>(bf.hair_flip.Value()), - .eye_type = static_cast<u8>(bf.eye_type.Value()), - .eye_color = static_cast<u8>(bf.eye_color.Value()), - .eye_scale = static_cast<u8>(bf.eye_scale.Value()), - .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()), - .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()), - .eye_x = static_cast<u8>(bf.eye_x.Value()), - .eye_y = static_cast<u8>(bf.eye_y.Value()), - .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()), - .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()), - .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()), - .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()), - .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()), - .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()), - .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3), - .nose_type = static_cast<u8>(bf.nose_type.Value()), - .nose_scale = static_cast<u8>(bf.nose_scale.Value()), - .nose_y = static_cast<u8>(bf.nose_y.Value()), - .mouth_type = static_cast<u8>(bf.mouth_type.Value()), - .mouth_color = static_cast<u8>(bf.mouth_color.Value()), - .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()), - .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()), - .mouth_y = static_cast<u8>(bf.mouth_y.Value()), - .beard_color = static_cast<u8>(bf.beard_color.Value()), - .beard_type = static_cast<u8>(bf.beard_type.Value()), - .mustache_type = static_cast<u8>(bf.mustache_type.Value()), - .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()), - .mustache_y = static_cast<u8>(bf.mustache_y.Value()), - .glasses_type = static_cast<u8>(bf.glasses_type.Value()), - .glasses_color = static_cast<u8>(bf.glasses_color.Value()), - .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()), - .glasses_y = static_cast<u8>(bf.glasses_y.Value()), - .mole_type = static_cast<u8>(bf.mole_type.Value()), - .mole_scale = static_cast<u8>(bf.mole_scale.Value()), - .mole_x = static_cast<u8>(bf.mole_x.Value()), - .mole_y = static_cast<u8>(bf.mole_y.Value()), - .padding = 0, - }; -} - -u16 GenerateCrc16(const void* data, std::size_t size) { - s32 crc{}; - for (std::size_t i = 0; i < size; i++) { - crc ^= static_cast<const u8*>(data)[i] << 8; - for (std::size_t j = 0; j < 8; j++) { - crc <<= 1; - if ((crc & 0x10000) != 0) { - crc = (crc ^ 0x1021) & 0xFFFF; - } - } - } - return Common::swap16(static_cast<u16>(crc)); -} - -Common::UUID GenerateValidUUID() { - auto uuid{Common::UUID::Generate()}; - - // Bit 7 must be set, and bit 6 unset for the UUID to be valid - uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL; - uuid.uuid[1] |= 0x0000000000000080ULL; - - return uuid; -} - -template <typename T> -T GetRandomValue(T min, T max) { - std::random_device device; - std::mt19937 gen(device()); - std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max)); - return static_cast<T>(distribution(gen)); -} - -template <typename T> -T GetRandomValue(T max) { - return GetRandomValue<T>({}, max); -} - -MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; - - if (gender == Gender::All) { - gender = GetRandomValue<Gender>(Gender::Maximum); - } - - bf.gender.Assign(gender); - bf.favorite_color.Assign(GetRandomValue<u8>(11)); - bf.region_move.Assign(0); - bf.font_region.Assign(FontRegion::Standard); - bf.type.Assign(0); - bf.height.Assign(64); - bf.build.Assign(64); - - if (age == Age::All) { - const auto temp{GetRandomValue<int>(10)}; - if (temp >= 8) { - age = Age::Old; - } else if (temp >= 4) { - age = Age::Normal; - } else { - age = Age::Young; - } - } - - if (race == Race::All) { - const auto temp{GetRandomValue<int>(10)}; - if (temp >= 8) { - race = Race::Black; - } else if (temp >= 4) { - race = Race::White; - } else { - race = Race::Asian; - } - } - - u32 axis_y{}; - if (gender == Gender::Female && age == Age::Young) { - axis_y = GetRandomValue<u32>(3); - } - - const std::size_t index{3 * static_cast<std::size_t>(age) + - 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; - - const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)}; - const auto faceline_color_info{RawData::RandomMiiFacelineColor.at( - 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; - const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; - const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; - const auto hair_type_info{RawData::RandomMiiHairType.at(index)}; - const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + - static_cast<std::size_t>(age))}; - const auto eye_type_info{RawData::RandomMiiEyeType.at(index)}; - const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; - const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; - const auto nose_type_info{RawData::RandomMiiNoseType.at(index)}; - const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)}; - const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; - - bf.faceline_type.Assign( - faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]); - bf.faceline_color.Assign( - faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]); - bf.faceline_wrinkle.Assign( - faceline_wrinkle_info - .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); - bf.faceline_makeup.Assign( - faceline_makeup_info - .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); - - bf.hair_type.Assign( - hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]); - bf.hair_color.Assign( - HairColorLookup[hair_color_info - .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]); - bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum)); - - bf.eye_type.Assign( - eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]); - - const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; - const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; - const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; - const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]}; - - bf.eye_color.Assign( - EyeColorLookup[eye_color_info - .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]); - bf.eye_scale.Assign(4); - bf.eye_aspect.Assign(3); - bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate); - bf.eye_x.Assign(2); - bf.eye_y.Assign(axis_y + 12); - - bf.eyebrow_type.Assign( - eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); - - const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; - const auto eyebrow_y{race == Race::Asian ? 9 : 10}; - const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6}; - const auto eyebrow_rotate{ - 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]}; - - bf.eyebrow_color.Assign(bf.hair_color); - bf.eyebrow_scale.Assign(4); - bf.eyebrow_aspect.Assign(3); - bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); - bf.eyebrow_x.Assign(2); - bf.eyebrow_y.Assign(axis_y + eyebrow_y); - - const auto nose_scale{gender == Gender::Female ? 3 : 4}; - - bf.nose_type.Assign( - nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]); - bf.nose_scale.Assign(nose_scale); - bf.nose_y.Assign(axis_y + 9); - - const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0}; - - bf.mouth_type.Assign( - mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]); - bf.mouth_color.Assign(MouthColorLookup[mouth_color]); - bf.mouth_scale.Assign(4); - bf.mouth_aspect.Assign(3); - bf.mouth_y.Assign(axis_y + 13); - - bf.beard_color.Assign(bf.hair_color); - bf.mustache_scale.Assign(4); - - if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) { - const auto mustache_and_beard_flag{ - GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)}; - - auto beard_type{BeardType::None}; - auto mustache_type{MustacheType::None}; - - if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == - BeardAndMustacheFlag::Beard) { - beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5); - } - - if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == - BeardAndMustacheFlag::Mustache) { - mustache_type = - GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5); - } - - bf.mustache_type.Assign(mustache_type); - bf.beard_type.Assign(beard_type); - bf.mustache_y.Assign(10); - } else { - bf.mustache_type.Assign(MustacheType::None); - bf.beard_type.Assign(BeardType::None); - bf.mustache_y.Assign(axis_y + 10); - } - - const auto glasses_type_start{GetRandomValue<std::size_t>(100)}; - u8 glasses_type{}; - while (glasses_type_start < glasses_type_info.values[glasses_type]) { - if (++glasses_type >= glasses_type_info.values_count) { - UNREACHABLE(); - break; - } - } - - bf.glasses_type.Assign(glasses_type); - bf.glasses_color.Assign(GlassesColorLookup[0]); - bf.glasses_scale.Assign(4); - bf.glasses_y.Assign(axis_y + 10); - - bf.mole_type.Assign(0); - bf.mole_scale.Assign(4); - bf.mole_x.Assign(2); - bf.mole_y.Assign(20); - - return {DefaultMiiName, bf, user_id}; -} - -MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { - MiiStoreBitFields bf{}; - - bf.font_region.Assign(info.font_region); - bf.favorite_color.Assign(info.favorite_color); - bf.gender.Assign(info.gender); - bf.height.Assign(info.height); - bf.build.Assign(info.weight); - bf.type.Assign(info.type); - bf.region_move.Assign(info.region); - bf.faceline_type.Assign(info.face_type); - bf.faceline_color.Assign(info.face_color); - bf.faceline_wrinkle.Assign(info.face_wrinkle); - bf.faceline_makeup.Assign(info.face_makeup); - bf.hair_type.Assign(info.hair_type); - bf.hair_color.Assign(HairColorLookup[info.hair_color]); - bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip)); - bf.eye_type.Assign(info.eye_type); - bf.eye_color.Assign(EyeColorLookup[info.eye_color]); - bf.eye_scale.Assign(info.eye_scale); - bf.eye_aspect.Assign(info.eye_aspect); - bf.eye_rotate.Assign(info.eye_rotate); - bf.eye_x.Assign(info.eye_x); - bf.eye_y.Assign(info.eye_y); - bf.eyebrow_type.Assign(info.eyebrow_type); - bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]); - bf.eyebrow_scale.Assign(info.eyebrow_scale); - bf.eyebrow_aspect.Assign(info.eyebrow_aspect); - bf.eyebrow_rotate.Assign(info.eyebrow_rotate); - bf.eyebrow_x.Assign(info.eyebrow_x); - bf.eyebrow_y.Assign(info.eyebrow_y - 3); - bf.nose_type.Assign(info.nose_type); - bf.nose_scale.Assign(info.nose_scale); - bf.nose_y.Assign(info.nose_y); - bf.mouth_type.Assign(info.mouth_type); - bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]); - bf.mouth_scale.Assign(info.mouth_scale); - bf.mouth_aspect.Assign(info.mouth_aspect); - bf.mouth_y.Assign(info.mouth_y); - bf.beard_color.Assign(HairColorLookup[info.beard_color]); - bf.beard_type.Assign(static_cast<BeardType>(info.beard_type)); - bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type)); - bf.mustache_scale.Assign(info.mustache_scale); - bf.mustache_y.Assign(info.mustache_y); - bf.glasses_type.Assign(info.glasses_type); - bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]); - bf.glasses_scale.Assign(info.glasses_scale); - bf.glasses_y.Assign(info.glasses_y); - bf.mole_type.Assign(info.mole_type); - bf.mole_scale.Assign(info.mole_scale); - bf.mole_x.Assign(info.mole_x); - bf.mole_y.Assign(info.mole_y); - - return {DefaultMiiName, bf, user_id}; -} - -} // namespace - -MiiStoreData::MiiStoreData() = default; - -MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, - const Common::UUID& user_id) { - data.name = name; - data.uuid = GenerateValidUUID(); - - std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); - data_crc = GenerateCrc16(data.data.data(), sizeof(data)); - device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID)); -} - -MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} - -bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) { - if ((source_flag & SourceFlag::Database) == SourceFlag::None) { - return false; - } - - const bool result{current_update_counter != update_counter}; - - current_update_counter = update_counter; - - return result; -} - -bool MiiManager::IsFullDatabase() const { - // TODO(bunnei): We don't implement the Mii database, so it cannot be full - return false; -} - -u32 MiiManager::GetCount(SourceFlag source_flag) const { - 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; - } - if ((source_flag & SourceFlag::Default) != SourceFlag::None) { - count += (DefaultMiiCount - BaseMiiCount); - } - return static_cast<u32>(count); -} - -ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, - SourceFlag source_flag) { - if ((source_flag & SourceFlag::Database) == SourceFlag::None) { - return ERROR_CANNOT_FIND_ENTRY; - } - - // TODO(bunnei): We don't implement the Mii database, so we can't have an entry - return ERROR_CANNOT_FIND_ENTRY; -} - -MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { - return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); -} - -MiiInfo MiiManager::BuildDefault(std::size_t index) { - return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); -} - -ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { - std::vector<MiiInfoElement> result; - - if ((source_flag & SourceFlag::Default) == SourceFlag::None) { - return MakeResult(std::move(result)); - } - - for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { - result.emplace_back(BuildDefault(index), Source::Default); - } - - return MakeResult(std::move(result)); -} - -ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { - constexpr u32 INVALID_INDEX{0xFFFFFFFF}; - - index = INVALID_INDEX; - - // TODO(bunnei): We don't implement the Mii database, so we can't have an index - return ERROR_CANNOT_FIND_ENTRY; -} - -} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index bbd81a88a..9d863486a 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -7,8 +7,8 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/mii/manager.h" #include "core/hle/service/mii/mii.h" +#include "core/hle/service/mii/mii_manager.h" #include "core/hle/service/service.h" #include "core/hle/service/sm/sm.h" diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp new file mode 100644 index 000000000..4fef2aea4 --- /dev/null +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -0,0 +1,465 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <random> + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" + +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/mii/mii_manager.h" +#include "core/hle/service/mii/raw_data.h" +#include "core/hle/service/mii/types.h" + +namespace Service::Mii { + +namespace { + +constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; + +constexpr std::size_t BaseMiiCount{2}; +constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; + +constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; +constexpr std::array<u8, 8> HairColorLookup{8, 1, 2, 3, 4, 5, 6, 7}; +constexpr std::array<u8, 6> EyeColorLookup{8, 9, 10, 11, 12, 13}; +constexpr std::array<u8, 5> MouthColorLookup{19, 20, 21, 22, 23}; +constexpr std::array<u8, 7> GlassesColorLookup{8, 14, 15, 16, 17, 18, 0}; +constexpr std::array<u8, 62> EyeRotateLookup{ + {0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x03, 0x04, 0x04}}; +constexpr std::array<u8, 24> EyebrowRotateLookup{{0x06, 0x06, 0x05, 0x07, 0x06, 0x07, 0x06, 0x07, + 0x04, 0x07, 0x06, 0x08, 0x05, 0x05, 0x06, 0x06, + 0x07, 0x07, 0x06, 0x06, 0x05, 0x06, 0x07, 0x05}}; + +template <typename T, std::size_t SourceArraySize, std::size_t DestArraySize> +std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& in) { + std::array<T, DestArraySize> out{}; + std::memcpy(out.data(), in.data(), sizeof(T) * std::min(SourceArraySize, DestArraySize)); + return out; +} + +MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { + MiiStoreBitFields bf; + std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); + + return { + .uuid = data.data.uuid, + .name = ResizeArray<char16_t, 10, 11>(data.data.name), + .font_region = static_cast<u8>(bf.font_region.Value()), + .favorite_color = static_cast<u8>(bf.favorite_color.Value()), + .gender = static_cast<u8>(bf.gender.Value()), + .height = static_cast<u8>(bf.height.Value()), + .build = static_cast<u8>(bf.build.Value()), + .type = static_cast<u8>(bf.type.Value()), + .region_move = static_cast<u8>(bf.region_move.Value()), + .faceline_type = static_cast<u8>(bf.faceline_type.Value()), + .faceline_color = static_cast<u8>(bf.faceline_color.Value()), + .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()), + .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()), + .hair_type = static_cast<u8>(bf.hair_type.Value()), + .hair_color = static_cast<u8>(bf.hair_color.Value()), + .hair_flip = static_cast<u8>(bf.hair_flip.Value()), + .eye_type = static_cast<u8>(bf.eye_type.Value()), + .eye_color = static_cast<u8>(bf.eye_color.Value()), + .eye_scale = static_cast<u8>(bf.eye_scale.Value()), + .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()), + .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()), + .eye_x = static_cast<u8>(bf.eye_x.Value()), + .eye_y = static_cast<u8>(bf.eye_y.Value()), + .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()), + .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()), + .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()), + .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()), + .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()), + .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()), + .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3), + .nose_type = static_cast<u8>(bf.nose_type.Value()), + .nose_scale = static_cast<u8>(bf.nose_scale.Value()), + .nose_y = static_cast<u8>(bf.nose_y.Value()), + .mouth_type = static_cast<u8>(bf.mouth_type.Value()), + .mouth_color = static_cast<u8>(bf.mouth_color.Value()), + .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()), + .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()), + .mouth_y = static_cast<u8>(bf.mouth_y.Value()), + .beard_color = static_cast<u8>(bf.beard_color.Value()), + .beard_type = static_cast<u8>(bf.beard_type.Value()), + .mustache_type = static_cast<u8>(bf.mustache_type.Value()), + .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()), + .mustache_y = static_cast<u8>(bf.mustache_y.Value()), + .glasses_type = static_cast<u8>(bf.glasses_type.Value()), + .glasses_color = static_cast<u8>(bf.glasses_color.Value()), + .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()), + .glasses_y = static_cast<u8>(bf.glasses_y.Value()), + .mole_type = static_cast<u8>(bf.mole_type.Value()), + .mole_scale = static_cast<u8>(bf.mole_scale.Value()), + .mole_x = static_cast<u8>(bf.mole_x.Value()), + .mole_y = static_cast<u8>(bf.mole_y.Value()), + .padding = 0, + }; +} + +u16 GenerateCrc16(const void* data, std::size_t size) { + s32 crc{}; + for (std::size_t i = 0; i < size; i++) { + crc ^= static_cast<const u8*>(data)[i] << 8; + for (std::size_t j = 0; j < 8; j++) { + crc <<= 1; + if ((crc & 0x10000) != 0) { + crc = (crc ^ 0x1021) & 0xFFFF; + } + } + } + return Common::swap16(static_cast<u16>(crc)); +} + +Common::UUID GenerateValidUUID() { + auto uuid{Common::UUID::Generate()}; + + // Bit 7 must be set, and bit 6 unset for the UUID to be valid + uuid.uuid[1] &= 0xFFFFFFFFFFFFFF3FULL; + uuid.uuid[1] |= 0x0000000000000080ULL; + + return uuid; +} + +template <typename T> +T GetRandomValue(T min, T max) { + std::random_device device; + std::mt19937 gen(device()); + std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max)); + return static_cast<T>(distribution(gen)); +} + +template <typename T> +T GetRandomValue(T max) { + return GetRandomValue<T>({}, max); +} + +MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Common::UUID& user_id) { + MiiStoreBitFields bf{}; + + if (gender == Gender::All) { + gender = GetRandomValue<Gender>(Gender::Maximum); + } + + bf.gender.Assign(gender); + bf.favorite_color.Assign(GetRandomValue<u8>(11)); + bf.region_move.Assign(0); + bf.font_region.Assign(FontRegion::Standard); + bf.type.Assign(0); + bf.height.Assign(64); + bf.build.Assign(64); + + if (age == Age::All) { + const auto temp{GetRandomValue<int>(10)}; + if (temp >= 8) { + age = Age::Old; + } else if (temp >= 4) { + age = Age::Normal; + } else { + age = Age::Young; + } + } + + if (race == Race::All) { + const auto temp{GetRandomValue<int>(10)}; + if (temp >= 8) { + race = Race::Black; + } else if (temp >= 4) { + race = Race::White; + } else { + race = Race::Asian; + } + } + + u32 axis_y{}; + if (gender == Gender::Female && age == Age::Young) { + axis_y = GetRandomValue<u32>(3); + } + + const std::size_t index{3 * static_cast<std::size_t>(age) + + 9 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race)}; + + const auto faceline_type_info{RawData::RandomMiiFaceline.at(index)}; + const auto faceline_color_info{RawData::RandomMiiFacelineColor.at( + 3 * static_cast<std::size_t>(gender) + static_cast<std::size_t>(race))}; + const auto faceline_wrinkle_info{RawData::RandomMiiFacelineWrinkle.at(index)}; + const auto faceline_makeup_info{RawData::RandomMiiFacelineMakeup.at(index)}; + const auto hair_type_info{RawData::RandomMiiHairType.at(index)}; + const auto hair_color_info{RawData::RandomMiiHairColor.at(3 * static_cast<std::size_t>(race) + + static_cast<std::size_t>(age))}; + const auto eye_type_info{RawData::RandomMiiEyeType.at(index)}; + const auto eye_color_info{RawData::RandomMiiEyeColor.at(static_cast<std::size_t>(race))}; + const auto eyebrow_type_info{RawData::RandomMiiEyebrowType.at(index)}; + const auto nose_type_info{RawData::RandomMiiNoseType.at(index)}; + const auto mouth_type_info{RawData::RandomMiiMouthType.at(index)}; + const auto glasses_type_info{RawData::RandomMiiGlassType.at(static_cast<std::size_t>(age))}; + + bf.faceline_type.Assign( + faceline_type_info.values[GetRandomValue<std::size_t>(faceline_type_info.values_count)]); + bf.faceline_color.Assign( + faceline_color_info.values[GetRandomValue<std::size_t>(faceline_color_info.values_count)]); + bf.faceline_wrinkle.Assign( + faceline_wrinkle_info + .values[GetRandomValue<std::size_t>(faceline_wrinkle_info.values_count)]); + bf.faceline_makeup.Assign( + faceline_makeup_info + .values[GetRandomValue<std::size_t>(faceline_makeup_info.values_count)]); + + bf.hair_type.Assign( + hair_type_info.values[GetRandomValue<std::size_t>(hair_type_info.values_count)]); + bf.hair_color.Assign( + HairColorLookup[hair_color_info + .values[GetRandomValue<std::size_t>(hair_color_info.values_count)]]); + bf.hair_flip.Assign(GetRandomValue<HairFlip>(HairFlip::Maximum)); + + bf.eye_type.Assign( + eye_type_info.values[GetRandomValue<std::size_t>(eye_type_info.values_count)]); + + const auto eye_rotate_1{gender != Gender::Male ? 4 : 2}; + const auto eye_rotate_2{gender != Gender::Male ? 3 : 4}; + const auto eye_rotate_offset{32 - EyeRotateLookup[eye_rotate_1] + eye_rotate_2}; + const auto eye_rotate{32 - EyeRotateLookup[bf.eye_type]}; + + bf.eye_color.Assign( + EyeColorLookup[eye_color_info + .values[GetRandomValue<std::size_t>(eye_color_info.values_count)]]); + bf.eye_scale.Assign(4); + bf.eye_aspect.Assign(3); + bf.eye_rotate.Assign(eye_rotate_offset - eye_rotate); + bf.eye_x.Assign(2); + bf.eye_y.Assign(axis_y + 12); + + bf.eyebrow_type.Assign( + eyebrow_type_info.values[GetRandomValue<std::size_t>(eyebrow_type_info.values_count)]); + + const auto eyebrow_rotate_1{race == Race::Asian ? 6 : 0}; + const auto eyebrow_y{race == Race::Asian ? 9 : 10}; + const auto eyebrow_rotate_offset{32 - EyebrowRotateLookup[eyebrow_rotate_1] + 6}; + const auto eyebrow_rotate{ + 32 - EyebrowRotateLookup[static_cast<std::size_t>(bf.eyebrow_type.Value())]}; + + bf.eyebrow_color.Assign(bf.hair_color); + bf.eyebrow_scale.Assign(4); + bf.eyebrow_aspect.Assign(3); + bf.eyebrow_rotate.Assign(eyebrow_rotate_offset - eyebrow_rotate); + bf.eyebrow_x.Assign(2); + bf.eyebrow_y.Assign(axis_y + eyebrow_y); + + const auto nose_scale{gender == Gender::Female ? 3 : 4}; + + bf.nose_type.Assign( + nose_type_info.values[GetRandomValue<std::size_t>(nose_type_info.values_count)]); + bf.nose_scale.Assign(nose_scale); + bf.nose_y.Assign(axis_y + 9); + + const auto mouth_color{gender == Gender::Female ? GetRandomValue<int>(4) : 0}; + + bf.mouth_type.Assign( + mouth_type_info.values[GetRandomValue<std::size_t>(mouth_type_info.values_count)]); + bf.mouth_color.Assign(MouthColorLookup[mouth_color]); + bf.mouth_scale.Assign(4); + bf.mouth_aspect.Assign(3); + bf.mouth_y.Assign(axis_y + 13); + + bf.beard_color.Assign(bf.hair_color); + bf.mustache_scale.Assign(4); + + if (gender == Gender::Male && age != Age::Young && GetRandomValue<int>(10) < 2) { + const auto mustache_and_beard_flag{ + GetRandomValue<BeardAndMustacheFlag>(BeardAndMustacheFlag::All)}; + + auto beard_type{BeardType::None}; + auto mustache_type{MustacheType::None}; + + if ((mustache_and_beard_flag & BeardAndMustacheFlag::Beard) == + BeardAndMustacheFlag::Beard) { + beard_type = GetRandomValue<BeardType>(BeardType::Beard1, BeardType::Beard5); + } + + if ((mustache_and_beard_flag & BeardAndMustacheFlag::Mustache) == + BeardAndMustacheFlag::Mustache) { + mustache_type = + GetRandomValue<MustacheType>(MustacheType::Mustache1, MustacheType::Mustache5); + } + + bf.mustache_type.Assign(mustache_type); + bf.beard_type.Assign(beard_type); + bf.mustache_y.Assign(10); + } else { + bf.mustache_type.Assign(MustacheType::None); + bf.beard_type.Assign(BeardType::None); + bf.mustache_y.Assign(axis_y + 10); + } + + const auto glasses_type_start{GetRandomValue<std::size_t>(100)}; + u8 glasses_type{}; + while (glasses_type_start < glasses_type_info.values[glasses_type]) { + if (++glasses_type >= glasses_type_info.values_count) { + UNREACHABLE(); + break; + } + } + + bf.glasses_type.Assign(glasses_type); + bf.glasses_color.Assign(GlassesColorLookup[0]); + bf.glasses_scale.Assign(4); + bf.glasses_y.Assign(axis_y + 10); + + bf.mole_type.Assign(0); + bf.mole_scale.Assign(4); + bf.mole_x.Assign(2); + bf.mole_y.Assign(20); + + return {DefaultMiiName, bf, user_id}; +} + +MiiStoreData BuildDefaultStoreData(const DefaultMii& info, const Common::UUID& user_id) { + MiiStoreBitFields bf{}; + + bf.font_region.Assign(info.font_region); + bf.favorite_color.Assign(info.favorite_color); + bf.gender.Assign(info.gender); + bf.height.Assign(info.height); + bf.build.Assign(info.weight); + bf.type.Assign(info.type); + bf.region_move.Assign(info.region); + bf.faceline_type.Assign(info.face_type); + bf.faceline_color.Assign(info.face_color); + bf.faceline_wrinkle.Assign(info.face_wrinkle); + bf.faceline_makeup.Assign(info.face_makeup); + bf.hair_type.Assign(info.hair_type); + bf.hair_color.Assign(HairColorLookup[info.hair_color]); + bf.hair_flip.Assign(static_cast<HairFlip>(info.hair_flip)); + bf.eye_type.Assign(info.eye_type); + bf.eye_color.Assign(EyeColorLookup[info.eye_color]); + bf.eye_scale.Assign(info.eye_scale); + bf.eye_aspect.Assign(info.eye_aspect); + bf.eye_rotate.Assign(info.eye_rotate); + bf.eye_x.Assign(info.eye_x); + bf.eye_y.Assign(info.eye_y); + bf.eyebrow_type.Assign(info.eyebrow_type); + bf.eyebrow_color.Assign(HairColorLookup[info.eyebrow_color]); + bf.eyebrow_scale.Assign(info.eyebrow_scale); + bf.eyebrow_aspect.Assign(info.eyebrow_aspect); + bf.eyebrow_rotate.Assign(info.eyebrow_rotate); + bf.eyebrow_x.Assign(info.eyebrow_x); + bf.eyebrow_y.Assign(info.eyebrow_y - 3); + bf.nose_type.Assign(info.nose_type); + bf.nose_scale.Assign(info.nose_scale); + bf.nose_y.Assign(info.nose_y); + bf.mouth_type.Assign(info.mouth_type); + bf.mouth_color.Assign(MouthColorLookup[info.mouth_color]); + bf.mouth_scale.Assign(info.mouth_scale); + bf.mouth_aspect.Assign(info.mouth_aspect); + bf.mouth_y.Assign(info.mouth_y); + bf.beard_color.Assign(HairColorLookup[info.beard_color]); + bf.beard_type.Assign(static_cast<BeardType>(info.beard_type)); + bf.mustache_type.Assign(static_cast<MustacheType>(info.mustache_type)); + bf.mustache_scale.Assign(info.mustache_scale); + bf.mustache_y.Assign(info.mustache_y); + bf.glasses_type.Assign(info.glasses_type); + bf.glasses_color.Assign(GlassesColorLookup[info.glasses_color]); + bf.glasses_scale.Assign(info.glasses_scale); + bf.glasses_y.Assign(info.glasses_y); + bf.mole_type.Assign(info.mole_type); + bf.mole_scale.Assign(info.mole_scale); + bf.mole_x.Assign(info.mole_x); + bf.mole_y.Assign(info.mole_y); + + return {DefaultMiiName, bf, user_id}; +} + +} // namespace + +MiiStoreData::MiiStoreData() = default; + +MiiStoreData::MiiStoreData(const MiiStoreData::Name& name, const MiiStoreBitFields& bit_fields, + const Common::UUID& user_id) { + data.name = name; + data.uuid = GenerateValidUUID(); + + std::memcpy(data.data.data(), &bit_fields, sizeof(MiiStoreBitFields)); + data_crc = GenerateCrc16(data.data.data(), sizeof(data)); + device_crc = GenerateCrc16(&user_id, sizeof(Common::UUID)); +} + +MiiManager::MiiManager() : user_id{Service::Account::ProfileManager().GetLastOpenedUser()} {} + +bool MiiManager::CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter) { + if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + return false; + } + + const bool result{current_update_counter != update_counter}; + + current_update_counter = update_counter; + + return result; +} + +bool MiiManager::IsFullDatabase() const { + // TODO(bunnei): We don't implement the Mii database, so it cannot be full + return false; +} + +u32 MiiManager::GetCount(SourceFlag source_flag) const { + 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; + } + if ((source_flag & SourceFlag::Default) != SourceFlag::None) { + count += (DefaultMiiCount - BaseMiiCount); + } + return static_cast<u32>(count); +} + +ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info, + SourceFlag source_flag) { + if ((source_flag & SourceFlag::Database) == SourceFlag::None) { + return ERROR_CANNOT_FIND_ENTRY; + } + + // TODO(bunnei): We don't implement the Mii database, so we can't have an entry + return ERROR_CANNOT_FIND_ENTRY; +} + +MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) { + return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id)); +} + +MiiInfo MiiManager::BuildDefault(std::size_t index) { + return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id)); +} + +ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) { + std::vector<MiiInfoElement> result; + + if ((source_flag & SourceFlag::Default) == SourceFlag::None) { + return MakeResult(std::move(result)); + } + + for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) { + result.emplace_back(BuildDefault(index), Source::Default); + } + + return MakeResult(std::move(result)); +} + +ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) { + constexpr u32 INVALID_INDEX{0xFFFFFFFF}; + + index = INVALID_INDEX; + + // TODO(bunnei): We don't implement the Mii database, so we can't have an index + return ERROR_CANNOT_FIND_ENTRY; +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/manager.h b/src/core/hle/service/mii/mii_manager.h index 8e048fc56..8e048fc56 100644 --- a/src/core/hle/service/mii/manager.h +++ b/src/core/hle/service/mii/mii_manager.h diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h index 0e35d69d2..a0d2b9d3a 100644 --- a/src/core/hle/service/mii/raw_data.h +++ b/src/core/hle/service/mii/raw_data.h @@ -7,7 +7,7 @@ #include <array> #include "common/common_types.h" -#include "core/hle/service/mii/manager.h" +#include "core/hle/service/mii/mii_manager.h" namespace Service::Mii::RawData { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index 98e6296f1..1403a39d0 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -19,26 +19,29 @@ namespace Service::Nvidia::Devices { namespace { -// Splice vectors will copy count amount of type T from the input vector into the dst vector. +// Copies count amount of type T from the input vector into the dst vector. +// Returns the number of bytes written into dst. template <typename T> -std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count, - std::size_t offset) { - if (!dst.empty()) { - std::memcpy(dst.data(), input.data() + offset, count * sizeof(T)); +std::size_t SliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count, + std::size_t offset) { + if (dst.empty()) { + return 0; } - return 0; + const size_t bytes_copied = count * sizeof(T); + std::memcpy(dst.data(), input.data() + offset, bytes_copied); + return bytes_copied; } -// Write vectors will write data to the output buffer +// Writes the data in src to an offset into the dst vector. The offset is specified in bytes +// Returns the number of bytes written into dst. template <typename T> std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) { if (src.empty()) { return 0; - } else { - std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T)); - offset += src.size() * sizeof(T); - return offset; } + const size_t bytes_copied = src.size() * sizeof(T); + std::memcpy(dst.data() + offset, src.data(), bytes_copied); + return bytes_copied; } } // Anonymous namespace @@ -62,7 +65,6 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count); // Instantiate param buffers - std::size_t offset = sizeof(IoctlSubmit); std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count); std::vector<Reloc> relocs(params.relocation_count); std::vector<u32> reloc_shifts(params.relocation_count); @@ -70,13 +72,14 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u std::vector<SyncptIncr> wait_checks(params.syncpoint_count); std::vector<Fence> fences(params.fence_count); - // Splice input into their respective buffers - offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset); - offset = SpliceVectors(input, relocs, params.relocation_count, offset); - offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset); - offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset); - offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset); - offset = SpliceVectors(input, fences, params.fence_count, offset); + // Slice input into their respective buffers + std::size_t offset = sizeof(IoctlSubmit); + offset += SliceVectors(input, command_buffers, params.cmd_buffer_count, offset); + offset += SliceVectors(input, relocs, params.relocation_count, offset); + offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset); + offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset); + offset += SliceVectors(input, wait_checks, params.syncpoint_count, offset); + offset += SliceVectors(input, fences, params.fence_count, offset); auto& gpu = system.GPU(); if (gpu.UseNvdec()) { @@ -88,35 +91,27 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u } } for (const auto& cmd_buffer : command_buffers) { - auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); + const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id); ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;); - const auto map = FindBufferMap(object->dma_map_addr); - if (!map) { - LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}", - object->addr, object->dma_map_addr); - return NvResult::Success; - } Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count); - gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(), - cmdlist.size() * sizeof(u32)); + system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(), + cmdlist.size() * sizeof(u32)); gpu.PushCommandBuffer(cmdlist); } if (gpu.UseNvdec()) { - fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1); - Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}}; gpu.PushCommandBuffer(cmdlist); } std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); // Some games expect command_buffers to be written back offset = sizeof(IoctlSubmit); - offset = WriteVectors(output, command_buffers, offset); - offset = WriteVectors(output, relocs, offset); - offset = WriteVectors(output, reloc_shifts, offset); - offset = WriteVectors(output, syncpt_increments, offset); - offset = WriteVectors(output, wait_checks, offset); - offset = WriteVectors(output, fences, offset); + offset += WriteVectors(output, command_buffers, offset); + offset += WriteVectors(output, relocs, offset); + offset += WriteVectors(output, reloc_shifts, offset); + offset += WriteVectors(output, syncpt_increments, offset); + offset += WriteVectors(output, wait_checks, offset); + offset += WriteVectors(output, fences, offset); return NvResult::Success; } @@ -148,14 +143,14 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); - SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); auto& gpu = system.GPU(); - for (auto& cmf_buff : cmd_buffer_handles) { - auto object{nvmap_dev->GetObject(cmf_buff.map_handle)}; + for (auto& cmd_buffer : cmd_buffer_handles) { + auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); + LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::InvalidState; } @@ -170,7 +165,7 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto if (!object->dma_map_addr) { LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size); } else { - cmf_buff.map_address = object->dma_map_addr; + cmd_buffer.map_address = object->dma_map_addr; AddBufferMap(object->dma_map_addr, object->size, object->addr, object->status == nvmap::Object::Status::Allocated); } @@ -186,14 +181,14 @@ NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vec IoctlMapBuffer params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries); - SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); + SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer)); auto& gpu = system.GPU(); - for (auto& cmf_buff : cmd_buffer_handles) { - const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)}; + for (auto& cmd_buffer : cmd_buffer_handles) { + const auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)}; if (!object) { - LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle); + LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle); std::memcpy(output.data(), ¶ms, output.size()); return NvResult::InvalidState; } diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp deleted file mode 100644 index e4d495000..000000000 --- a/src/core/hle/service/nvdrv/interface.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cinttypes> -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/k_writable_event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/nvdrv/interface.h" -#include "core/hle/service/nvdrv/nvdata.h" -#include "core/hle/service/nvdrv/nvdrv.h" - -namespace Service::Nvidia { - -void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { - nvdrv->SignalSyncpt(syncpoint_id, value); -} - -void NVDRV::Open(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - - if (!is_initialized) { - rb.Push<DeviceFD>(0); - rb.PushEnum(NvResult::NotInitialized); - - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - const auto& buffer = ctx.ReadBuffer(); - const std::string device_name(buffer.begin(), buffer.end()); - - if (device_name == "/dev/nvhost-prof-gpu") { - rb.Push<DeviceFD>(0); - rb.PushEnum(NvResult::NotSupported); - - LOG_WARNING(Service_NVDRV, "/dev/nvhost-prof-gpu cannot be opened in production"); - return; - } - - DeviceFD fd = nvdrv->Open(device_name); - - rb.Push<DeviceFD>(fd); - rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); -} - -void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(result); -} - -void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto fd = rp.Pop<DeviceFD>(); - const auto command = rp.PopRaw<Ioctl>(); - LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); - - if (!is_initialized) { - ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - // Check device - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); - const auto input_buffer = ctx.ReadBuffer(0); - - const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); - if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nv_result); -} - -void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto fd = rp.Pop<DeviceFD>(); - const auto command = rp.PopRaw<Ioctl>(); - LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); - - if (!is_initialized) { - ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - const auto input_buffer = ctx.ReadBuffer(0); - const auto input_inlined_buffer = ctx.ReadBuffer(1); - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); - - const auto nv_result = - nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); - if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nv_result); -} - -void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto fd = rp.Pop<DeviceFD>(); - const auto command = rp.PopRaw<Ioctl>(); - LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); - - if (!is_initialized) { - ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - const auto input_buffer = ctx.ReadBuffer(0); - std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); - std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); - - const auto nv_result = - nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); - if (command.is_out != 0) { - ctx.WriteBuffer(output_buffer, 0); - ctx.WriteBuffer(output_buffer_inline, 1); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(nv_result); -} - -void NVDRV::Close(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - - if (!is_initialized) { - ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - IPC::RequestParser rp{ctx}; - const auto fd = rp.Pop<DeviceFD>(); - const auto result = nvdrv->Close(fd); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(result); -} - -void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - - is_initialized = true; - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::Success); -} - -void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto fd = rp.Pop<DeviceFD>(); - const auto event_id = rp.Pop<u32>() & 0x00FF; - LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); - - if (!is_initialized) { - ServiceError(ctx, NvResult::NotInitialized); - LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); - return; - } - - const auto nv_result = nvdrv->VerifyFD(fd); - if (nv_result != NvResult::Success) { - LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); - ServiceError(ctx, nv_result); - return; - } - - if (event_id < MaxNvEvents) { - IPC::ResponseBuilder rb{ctx, 3, 1}; - rb.Push(ResultSuccess); - auto& event = nvdrv->GetEvent(event_id); - event.Clear(); - rb.PushCopyObjects(event); - rb.PushEnum(NvResult::Success); - } else { - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::BadParameter); - } -} - -void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - pid = rp.Pop<u64>(); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::Success); -} - -void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(NvResult::Success); -} - -void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { - // According to SwitchBrew, this has no inputs and no outputs, so effectively does nothing on - // retail hardware. - LOG_DEBUG(Service_NVDRV, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) - : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { - static const FunctionInfo functions[] = { - {0, &NVDRV::Open, "Open"}, - {1, &NVDRV::Ioctl1, "Ioctl"}, - {2, &NVDRV::Close, "Close"}, - {3, &NVDRV::Initialize, "Initialize"}, - {4, &NVDRV::QueryEvent, "QueryEvent"}, - {5, nullptr, "MapSharedMem"}, - {6, &NVDRV::GetStatus, "GetStatus"}, - {7, nullptr, "SetAruidForTest"}, - {8, &NVDRV::SetAruid, "SetAruid"}, - {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, - {10, nullptr, "InitializeDevtools"}, - {11, &NVDRV::Ioctl2, "Ioctl2"}, - {12, &NVDRV::Ioctl3, "Ioctl3"}, - {13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled, - "SetGraphicsFirmwareMemoryMarginEnabled"}, - }; - RegisterHandlers(functions); -} - -NVDRV::~NVDRV() = default; - -} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 74796dce1..ff405099a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -20,8 +20,8 @@ #include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h" #include "core/hle/service/nvdrv/devices/nvhost_vic.h" #include "core/hle/service/nvdrv/devices/nvmap.h" -#include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" #include "core/hle/service/nvdrv/nvmemp.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -39,11 +39,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger nvflinger.SetNVDrvInstance(module_); } -Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { - auto& kernel = system.Kernel(); +Module::Module(Core::System& system) + : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} { for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event = Kernel::KEvent::Create(kernel); - events_interface.events[i].event->Initialize(fmt::format("NVDRV::NvEvent_{}", i)); + events_interface.events[i].event = + service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i)); events_interface.status[i] = EventState::Free; events_interface.registered[i] = false; } @@ -65,8 +65,7 @@ Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { Module::~Module() { for (u32 i = 0; i < MaxNvEvents; i++) { - events_interface.events[i].event->Close(); - events_interface.events[i].event = nullptr; + service_context.CloseEvent(events_interface.events[i].event); } } diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a43ceb7ae..e2a1dde5b 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -9,6 +9,7 @@ #include <vector> #include "common/common_types.h" +#include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/syncpoint_manager.h" #include "core/hle/service/service.h" @@ -154,6 +155,8 @@ private: std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; EventInterface events_interface; + + KernelHelpers::ServiceContext service_context; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp new file mode 100644 index 000000000..d61fb73dc --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp @@ -0,0 +1,259 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cinttypes> +#include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_readable_event.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/k_writable_event.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/nvdrv/nvdrv_interface.h" + +namespace Service::Nvidia { + +void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { + nvdrv->SignalSyncpt(syncpoint_id, value); +} + +void NVDRV::Open(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + + if (!is_initialized) { + rb.Push<DeviceFD>(0); + rb.PushEnum(NvResult::NotInitialized); + + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto& buffer = ctx.ReadBuffer(); + const std::string device_name(buffer.begin(), buffer.end()); + + if (device_name == "/dev/nvhost-prof-gpu") { + rb.Push<DeviceFD>(0); + rb.PushEnum(NvResult::NotSupported); + + LOG_WARNING(Service_NVDRV, "/dev/nvhost-prof-gpu cannot be opened in production"); + return; + } + + DeviceFD fd = nvdrv->Open(device_name); + + rb.Push<DeviceFD>(fd); + rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed); +} + +void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(result); +} + +void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + // Check device + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + const auto input_buffer = ctx.ReadBuffer(0); + + const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer); + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(nv_result); +} + +void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto input_buffer = ctx.ReadBuffer(0); + const auto input_inlined_buffer = ctx.ReadBuffer(1); + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + + const auto nv_result = + nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer); + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(nv_result); +} + +void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto command = rp.PopRaw<Ioctl>(); + LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto input_buffer = ctx.ReadBuffer(0); + std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0)); + std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1)); + + const auto nv_result = + nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline); + if (command.is_out != 0) { + ctx.WriteBuffer(output_buffer, 0); + ctx.WriteBuffer(output_buffer_inline, 1); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(nv_result); +} + +void NVDRV::Close(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto result = nvdrv->Close(fd); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(result); +} + +void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + is_initialized = true; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::Success); +} + +void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto fd = rp.Pop<DeviceFD>(); + const auto event_id = rp.Pop<u32>() & 0x00FF; + LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); + + if (!is_initialized) { + ServiceError(ctx, NvResult::NotInitialized); + LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); + return; + } + + const auto nv_result = nvdrv->VerifyFD(fd); + if (nv_result != NvResult::Success) { + LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd); + ServiceError(ctx, nv_result); + return; + } + + if (event_id < MaxNvEvents) { + IPC::ResponseBuilder rb{ctx, 3, 1}; + rb.Push(ResultSuccess); + auto& event = nvdrv->GetEvent(event_id); + event.Clear(); + rb.PushCopyObjects(event); + rb.PushEnum(NvResult::Success); + } else { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::BadParameter); + } +} + +void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + pid = rp.Pop<u64>(); + LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::Success); +} + +void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NVDRV, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(NvResult::Success); +} + +void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) { + // According to SwitchBrew, this has no inputs and no outputs, so effectively does nothing on + // retail hardware. + LOG_DEBUG(Service_NVDRV, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) + : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { + static const FunctionInfo functions[] = { + {0, &NVDRV::Open, "Open"}, + {1, &NVDRV::Ioctl1, "Ioctl"}, + {2, &NVDRV::Close, "Close"}, + {3, &NVDRV::Initialize, "Initialize"}, + {4, &NVDRV::QueryEvent, "QueryEvent"}, + {5, nullptr, "MapSharedMem"}, + {6, &NVDRV::GetStatus, "GetStatus"}, + {7, nullptr, "SetAruidForTest"}, + {8, &NVDRV::SetAruid, "SetAruid"}, + {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, + {10, nullptr, "InitializeDevtools"}, + {11, &NVDRV::Ioctl2, "Ioctl2"}, + {12, &NVDRV::Ioctl3, "Ioctl3"}, + {13, &NVDRV::SetGraphicsFirmwareMemoryMarginEnabled, + "SetGraphicsFirmwareMemoryMarginEnabled"}, + }; + RegisterHandlers(functions); +} + +NVDRV::~NVDRV() = default; + +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h index 0e764c53f..0e764c53f 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/nvdrv_interface.h diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp deleted file mode 100644 index 1e31d05a6..000000000 --- a/src/core/hle/service/pctl/module.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/logging/log.h" -#include "core/core.h" -#include "core/file_sys/control_metadata.h" -#include "core/file_sys/patch_manager.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/service/pctl/module.h" -#include "core/hle/service/pctl/pctl.h" - -namespace Service::PCTL { - -namespace Error { - -constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; -constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; -constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131}; -constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; - -} // namespace Error - -class IParentalControlService final : public ServiceFramework<IParentalControlService> { -public: - explicit IParentalControlService(Core::System& system_, Capability capability_) - : ServiceFramework{system_, "IParentalControlService"}, capability{capability_} { - // clang-format off - static const FunctionInfo functions[] = { - {1, &IParentalControlService::Initialize, "Initialize"}, - {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, - {1002, nullptr, "ConfirmLaunchApplicationPermission"}, - {1003, nullptr, "ConfirmResumeApplicationPermission"}, - {1004, nullptr, "ConfirmSnsPostPermission"}, - {1005, nullptr, "ConfirmSystemSettingsPermission"}, - {1006, nullptr, "IsRestrictionTemporaryUnlocked"}, - {1007, nullptr, "RevertRestrictionTemporaryUnlocked"}, - {1008, nullptr, "EnterRestrictedSystemSettings"}, - {1009, nullptr, "LeaveRestrictedSystemSettings"}, - {1010, nullptr, "IsRestrictedSystemSettingsEntered"}, - {1011, nullptr, "RevertRestrictedSystemSettingsEntered"}, - {1012, nullptr, "GetRestrictedFeatures"}, - {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"}, - {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, - {1015, nullptr, "ConfirmPlayableApplicationVideo"}, - {1016, nullptr, "ConfirmShowNewsPermission"}, - {1017, nullptr, "EndFreeCommunication"}, - {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"}, - {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"}, - {1032, nullptr, "GetSafetyLevel"}, - {1033, nullptr, "SetSafetyLevel"}, - {1034, nullptr, "GetSafetyLevelSettings"}, - {1035, nullptr, "GetCurrentSettings"}, - {1036, nullptr, "SetCustomSafetyLevelSettings"}, - {1037, nullptr, "GetDefaultRatingOrganization"}, - {1038, nullptr, "SetDefaultRatingOrganization"}, - {1039, nullptr, "GetFreeCommunicationApplicationListCount"}, - {1042, nullptr, "AddToFreeCommunicationApplicationList"}, - {1043, nullptr, "DeleteSettings"}, - {1044, nullptr, "GetFreeCommunicationApplicationList"}, - {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, - {1046, nullptr, "DisableFeaturesForReset"}, - {1047, nullptr, "NotifyApplicationDownloadStarted"}, - {1048, nullptr, "NotifyNetworkProfileCreated"}, - {1049, nullptr, "ResetFreeCommunicationApplicationList"}, - {1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"}, - {1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"}, - {1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"}, - {1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"}, - {1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"}, - {1201, nullptr, "UnlockRestrictionTemporarily"}, - {1202, nullptr, "UnlockSystemSettingsRestriction"}, - {1203, nullptr, "SetPinCode"}, - {1204, nullptr, "GenerateInquiryCode"}, - {1205, nullptr, "CheckMasterKey"}, - {1206, nullptr, "GetPinCodeLength"}, - {1207, nullptr, "GetPinCodeChangedEvent"}, - {1208, nullptr, "GetPinCode"}, - {1403, nullptr, "IsPairingActive"}, - {1406, nullptr, "GetSettingsLastUpdated"}, - {1411, nullptr, "GetPairingAccountInfo"}, - {1421, nullptr, "GetAccountNickname"}, - {1424, nullptr, "GetAccountState"}, - {1425, nullptr, "RequestPostEvents"}, - {1426, nullptr, "GetPostEventInterval"}, - {1427, nullptr, "SetPostEventInterval"}, - {1432, nullptr, "GetSynchronizationEvent"}, - {1451, nullptr, "StartPlayTimer"}, - {1452, nullptr, "StopPlayTimer"}, - {1453, nullptr, "IsPlayTimerEnabled"}, - {1454, nullptr, "GetPlayTimerRemainingTime"}, - {1455, nullptr, "IsRestrictedByPlayTimer"}, - {1456, nullptr, "GetPlayTimerSettings"}, - {1457, nullptr, "GetPlayTimerEventToRequestSuspension"}, - {1458, nullptr, "IsPlayTimerAlarmDisabled"}, - {1471, nullptr, "NotifyWrongPinCodeInputManyTimes"}, - {1472, nullptr, "CancelNetworkRequest"}, - {1473, nullptr, "GetUnlinkedEvent"}, - {1474, nullptr, "ClearUnlinkedEvent"}, - {1601, nullptr, "DisableAllFeatures"}, - {1602, nullptr, "PostEnableAllFeatures"}, - {1603, nullptr, "IsAllFeaturesDisabled"}, - {1901, nullptr, "DeleteFromFreeCommunicationApplicationListForDebug"}, - {1902, nullptr, "ClearFreeCommunicationApplicationListForDebug"}, - {1903, nullptr, "GetExemptApplicationListCountForDebug"}, - {1904, nullptr, "GetExemptApplicationListForDebug"}, - {1905, nullptr, "UpdateExemptApplicationListForDebug"}, - {1906, nullptr, "AddToExemptApplicationListForDebug"}, - {1907, nullptr, "DeleteFromExemptApplicationListForDebug"}, - {1908, nullptr, "ClearExemptApplicationListForDebug"}, - {1941, nullptr, "DeletePairing"}, - {1951, nullptr, "SetPlayTimerSettingsForDebug"}, - {1952, nullptr, "GetPlayTimerSpentTimeForTest"}, - {1953, nullptr, "SetPlayTimerAlarmDisabledForDebug"}, - {2001, nullptr, "RequestPairingAsync"}, - {2002, nullptr, "FinishRequestPairing"}, - {2003, nullptr, "AuthorizePairingAsync"}, - {2004, nullptr, "FinishAuthorizePairing"}, - {2005, nullptr, "RetrievePairingInfoAsync"}, - {2006, nullptr, "FinishRetrievePairingInfo"}, - {2007, nullptr, "UnlinkPairingAsync"}, - {2008, nullptr, "FinishUnlinkPairing"}, - {2009, nullptr, "GetAccountMiiImageAsync"}, - {2010, nullptr, "FinishGetAccountMiiImage"}, - {2011, nullptr, "GetAccountMiiImageContentTypeAsync"}, - {2012, nullptr, "FinishGetAccountMiiImageContentType"}, - {2013, nullptr, "SynchronizeParentalControlSettingsAsync"}, - {2014, nullptr, "FinishSynchronizeParentalControlSettings"}, - {2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"}, - {2016, nullptr, "RequestUpdateExemptionListAsync"}, - }; - // clang-format on - RegisterHandlers(functions); - } - -private: - bool CheckFreeCommunicationPermissionImpl() const { - if (states.temporary_unlocked) { - return true; - } - if ((states.application_info.parental_control_flag & 1) == 0) { - return true; - } - if (pin_code[0] == '\0') { - return true; - } - if (!settings.is_free_communication_default_on) { - return true; - } - // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here - // but as we don't have multiproceses support yet, we can just assume our application is - // valid for the time being - return true; - } - - bool ConfirmStereoVisionPermissionImpl() const { - if (states.temporary_unlocked) { - return true; - } - if (pin_code[0] == '\0') { - return true; - } - if (!settings.is_stero_vision_restricted) { - return false; - } - return true; - } - - void SetStereoVisionRestrictionImpl(bool is_restricted) { - if (settings.disabled) { - return; - } - - if (pin_code[0] == '\0') { - return; - } - settings.is_stero_vision_restricted = is_restricted; - } - - void Initialize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - IPC::ResponseBuilder rb{ctx, 2}; - - if (False(capability & (Capability::Application | Capability::System))) { - LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", capability); - return; - } - - // TODO(ogniK): Recovery flag initialization for pctl:r - - const auto tid = system.CurrentProcess()->GetTitleID(); - if (tid != 0) { - const FileSys::PatchManager pm{tid, system.GetFileSystemController(), - system.GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - if (control.first) { - states.tid_from_event = 0; - states.launch_time_valid = false; - states.is_suspended = false; - states.free_communication = false; - states.stereo_vision = false; - states.application_info = ApplicationInfo{ - .tid = tid, - .age_rating = control.first->GetRatingAge(), - .parental_control_flag = control.first->GetParentalControlFlag(), - .capability = capability, - }; - - if (False(capability & (Capability::System | Capability::Recovery))) { - // TODO(ogniK): Signal application launch event - } - } - } - - rb.Push(ResultSuccess); - } - - void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - if (!CheckFreeCommunicationPermissionImpl()) { - rb.Push(Error::ResultNoFreeCommunication); - } else { - rb.Push(ResultSuccess); - } - - states.free_communication = true; - } - - void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - states.stereo_vision = true; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - if (!CheckFreeCommunicationPermissionImpl()) { - rb.Push(Error::ResultNoFreeCommunication); - } else { - rb.Push(ResultSuccess); - } - } - - void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - if (False(capability & (Capability::Status | Capability::Recovery))) { - LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!"); - rb.Push(Error::ResultNoCapability); - rb.Push(false); - return; - } - - rb.Push(pin_code[0] != '\0'); - } - - void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - - if (False(capability & Capability::StereoVision)) { - LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); - rb.Push(Error::ResultNoCapability); - return; - } - - if (pin_code[0] == '\0') { - rb.Push(Error::ResultNoRestrictionEnabled); - return; - } - - rb.Push(ResultSuccess); - } - - void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - if (!ConfirmStereoVisionPermissionImpl()) { - rb.Push(Error::ResultStereoVisionRestricted); - rb.Push(false); - } else { - rb.Push(ResultSuccess); - rb.Push(true); - } - } - - void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto can_use = rp.Pop<bool>(); - LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use); - - IPC::ResponseBuilder rb{ctx, 2}; - if (False(capability & Capability::StereoVision)) { - LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); - rb.Push(Error::ResultNoCapability); - return; - } - - SetStereoVisionRestrictionImpl(can_use); - rb.Push(ResultSuccess); - } - - void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 3}; - if (False(capability & Capability::StereoVision)) { - LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); - rb.Push(Error::ResultNoCapability); - rb.Push(false); - return; - } - - rb.Push(ResultSuccess); - rb.Push(settings.is_stero_vision_restricted); - } - - void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - states.stereo_vision = false; - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - struct ApplicationInfo { - u64 tid{}; - std::array<u8, 32> age_rating{}; - u32 parental_control_flag{}; - Capability capability{}; - }; - - struct States { - u64 current_tid{}; - ApplicationInfo application_info{}; - u64 tid_from_event{}; - bool launch_time_valid{}; - bool is_suspended{}; - bool temporary_unlocked{}; - bool free_communication{}; - bool stereo_vision{}; - }; - - struct ParentalControlSettings { - bool is_stero_vision_restricted{}; - bool is_free_communication_default_on{}; - bool disabled{}; - }; - - States states{}; - ParentalControlSettings settings{}; - std::array<char, 8> pin_code{}; - Capability capability{}; -}; - -void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - // TODO(ogniK): Get TID from process - - rb.PushIpcInterface<IParentalControlService>(system, capability); -} - -void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_PCTL, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface<IParentalControlService>(system, capability); -} - -Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, - const char* name_, Capability capability_) - : ServiceFramework{system_, name_}, module{std::move(module_)}, capability{capability_} {} - -Module::Interface::~Interface() = default; - -void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared<Module>(); - std::make_shared<PCTL>(system, module, "pctl", - Capability::Application | Capability::SnsPost | Capability::Status | - Capability::StereoVision) - ->InstallAsService(service_manager); - // TODO(ogniK): Implement remaining capabilities - std::make_shared<PCTL>(system, module, "pctl:a", Capability::None) - ->InstallAsService(service_manager); - std::make_shared<PCTL>(system, module, "pctl:r", Capability::None) - ->InstallAsService(service_manager); - std::make_shared<PCTL>(system, module, "pctl:s", Capability::None) - ->InstallAsService(service_manager); -} - -} // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index ea3b97823..1d28900b2 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/pctl/module.h" +#include "core/hle/service/pctl/pctl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp new file mode 100644 index 000000000..6949fcf3b --- /dev/null +++ b/src/core/hle/service/pctl/pctl_module.cpp @@ -0,0 +1,406 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_process.h" +#include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pctl/pctl_module.h" + +namespace Service::PCTL { + +namespace Error { + +constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; +constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; +constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131}; +constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; + +} // namespace Error + +class IParentalControlService final : public ServiceFramework<IParentalControlService> { +public: + explicit IParentalControlService(Core::System& system_, Capability capability_) + : ServiceFramework{system_, "IParentalControlService"}, capability{capability_} { + // clang-format off + static const FunctionInfo functions[] = { + {1, &IParentalControlService::Initialize, "Initialize"}, + {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"}, + {1002, nullptr, "ConfirmLaunchApplicationPermission"}, + {1003, nullptr, "ConfirmResumeApplicationPermission"}, + {1004, nullptr, "ConfirmSnsPostPermission"}, + {1005, nullptr, "ConfirmSystemSettingsPermission"}, + {1006, nullptr, "IsRestrictionTemporaryUnlocked"}, + {1007, nullptr, "RevertRestrictionTemporaryUnlocked"}, + {1008, nullptr, "EnterRestrictedSystemSettings"}, + {1009, nullptr, "LeaveRestrictedSystemSettings"}, + {1010, nullptr, "IsRestrictedSystemSettingsEntered"}, + {1011, nullptr, "RevertRestrictedSystemSettingsEntered"}, + {1012, nullptr, "GetRestrictedFeatures"}, + {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"}, + {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, + {1015, nullptr, "ConfirmPlayableApplicationVideo"}, + {1016, nullptr, "ConfirmShowNewsPermission"}, + {1017, nullptr, "EndFreeCommunication"}, + {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"}, + {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"}, + {1032, nullptr, "GetSafetyLevel"}, + {1033, nullptr, "SetSafetyLevel"}, + {1034, nullptr, "GetSafetyLevelSettings"}, + {1035, nullptr, "GetCurrentSettings"}, + {1036, nullptr, "SetCustomSafetyLevelSettings"}, + {1037, nullptr, "GetDefaultRatingOrganization"}, + {1038, nullptr, "SetDefaultRatingOrganization"}, + {1039, nullptr, "GetFreeCommunicationApplicationListCount"}, + {1042, nullptr, "AddToFreeCommunicationApplicationList"}, + {1043, nullptr, "DeleteSettings"}, + {1044, nullptr, "GetFreeCommunicationApplicationList"}, + {1045, nullptr, "UpdateFreeCommunicationApplicationList"}, + {1046, nullptr, "DisableFeaturesForReset"}, + {1047, nullptr, "NotifyApplicationDownloadStarted"}, + {1048, nullptr, "NotifyNetworkProfileCreated"}, + {1049, nullptr, "ResetFreeCommunicationApplicationList"}, + {1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"}, + {1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"}, + {1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"}, + {1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"}, + {1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"}, + {1201, nullptr, "UnlockRestrictionTemporarily"}, + {1202, nullptr, "UnlockSystemSettingsRestriction"}, + {1203, nullptr, "SetPinCode"}, + {1204, nullptr, "GenerateInquiryCode"}, + {1205, nullptr, "CheckMasterKey"}, + {1206, nullptr, "GetPinCodeLength"}, + {1207, nullptr, "GetPinCodeChangedEvent"}, + {1208, nullptr, "GetPinCode"}, + {1403, nullptr, "IsPairingActive"}, + {1406, nullptr, "GetSettingsLastUpdated"}, + {1411, nullptr, "GetPairingAccountInfo"}, + {1421, nullptr, "GetAccountNickname"}, + {1424, nullptr, "GetAccountState"}, + {1425, nullptr, "RequestPostEvents"}, + {1426, nullptr, "GetPostEventInterval"}, + {1427, nullptr, "SetPostEventInterval"}, + {1432, nullptr, "GetSynchronizationEvent"}, + {1451, nullptr, "StartPlayTimer"}, + {1452, nullptr, "StopPlayTimer"}, + {1453, nullptr, "IsPlayTimerEnabled"}, + {1454, nullptr, "GetPlayTimerRemainingTime"}, + {1455, nullptr, "IsRestrictedByPlayTimer"}, + {1456, nullptr, "GetPlayTimerSettings"}, + {1457, nullptr, "GetPlayTimerEventToRequestSuspension"}, + {1458, nullptr, "IsPlayTimerAlarmDisabled"}, + {1471, nullptr, "NotifyWrongPinCodeInputManyTimes"}, + {1472, nullptr, "CancelNetworkRequest"}, + {1473, nullptr, "GetUnlinkedEvent"}, + {1474, nullptr, "ClearUnlinkedEvent"}, + {1601, nullptr, "DisableAllFeatures"}, + {1602, nullptr, "PostEnableAllFeatures"}, + {1603, nullptr, "IsAllFeaturesDisabled"}, + {1901, nullptr, "DeleteFromFreeCommunicationApplicationListForDebug"}, + {1902, nullptr, "ClearFreeCommunicationApplicationListForDebug"}, + {1903, nullptr, "GetExemptApplicationListCountForDebug"}, + {1904, nullptr, "GetExemptApplicationListForDebug"}, + {1905, nullptr, "UpdateExemptApplicationListForDebug"}, + {1906, nullptr, "AddToExemptApplicationListForDebug"}, + {1907, nullptr, "DeleteFromExemptApplicationListForDebug"}, + {1908, nullptr, "ClearExemptApplicationListForDebug"}, + {1941, nullptr, "DeletePairing"}, + {1951, nullptr, "SetPlayTimerSettingsForDebug"}, + {1952, nullptr, "GetPlayTimerSpentTimeForTest"}, + {1953, nullptr, "SetPlayTimerAlarmDisabledForDebug"}, + {2001, nullptr, "RequestPairingAsync"}, + {2002, nullptr, "FinishRequestPairing"}, + {2003, nullptr, "AuthorizePairingAsync"}, + {2004, nullptr, "FinishAuthorizePairing"}, + {2005, nullptr, "RetrievePairingInfoAsync"}, + {2006, nullptr, "FinishRetrievePairingInfo"}, + {2007, nullptr, "UnlinkPairingAsync"}, + {2008, nullptr, "FinishUnlinkPairing"}, + {2009, nullptr, "GetAccountMiiImageAsync"}, + {2010, nullptr, "FinishGetAccountMiiImage"}, + {2011, nullptr, "GetAccountMiiImageContentTypeAsync"}, + {2012, nullptr, "FinishGetAccountMiiImageContentType"}, + {2013, nullptr, "SynchronizeParentalControlSettingsAsync"}, + {2014, nullptr, "FinishSynchronizeParentalControlSettings"}, + {2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"}, + {2016, nullptr, "RequestUpdateExemptionListAsync"}, + }; + // clang-format on + RegisterHandlers(functions); + } + +private: + bool CheckFreeCommunicationPermissionImpl() const { + if (states.temporary_unlocked) { + return true; + } + if ((states.application_info.parental_control_flag & 1) == 0) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_free_communication_default_on) { + return true; + } + // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here + // but as we don't have multiproceses support yet, we can just assume our application is + // valid for the time being + return true; + } + + bool ConfirmStereoVisionPermissionImpl() const { + if (states.temporary_unlocked) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_stero_vision_restricted) { + return false; + } + return true; + } + + void SetStereoVisionRestrictionImpl(bool is_restricted) { + if (settings.disabled) { + return; + } + + if (pin_code[0] == '\0') { + return; + } + settings.is_stero_vision_restricted = is_restricted; + } + + void Initialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & (Capability::Application | Capability::System))) { + LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", capability); + return; + } + + // TODO(ogniK): Recovery flag initialization for pctl:r + + const auto tid = system.CurrentProcess()->GetTitleID(); + if (tid != 0) { + const FileSys::PatchManager pm{tid, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + if (control.first) { + states.tid_from_event = 0; + states.launch_time_valid = false; + states.is_suspended = false; + states.free_communication = false; + states.stereo_vision = false; + states.application_info = ApplicationInfo{ + .tid = tid, + .age_rating = control.first->GetRatingAge(), + .parental_control_flag = control.first->GetParentalControlFlag(), + .capability = capability, + }; + + if (False(capability & (Capability::System | Capability::Recovery))) { + // TODO(ogniK): Signal application launch event + } + } + } + + rb.Push(ResultSuccess); + } + + void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(ResultSuccess); + } + + states.free_communication = true; + } + + void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + states.stereo_vision = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PCTL, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(ResultSuccess); + } + } + + void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & (Capability::Status | Capability::Recovery))) { + LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!"); + rb.Push(Error::ResultNoCapability); + rb.Push(false); + return; + } + + rb.Push(pin_code[0] != '\0'); + } + + void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + return; + } + + if (pin_code[0] == '\0') { + rb.Push(Error::ResultNoRestrictionEnabled); + return; + } + + rb.Push(ResultSuccess); + } + + void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + if (!ConfirmStereoVisionPermissionImpl()) { + rb.Push(Error::ResultStereoVisionRestricted); + rb.Push(false); + } else { + rb.Push(ResultSuccess); + rb.Push(true); + } + } + + void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto can_use = rp.Pop<bool>(); + LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use); + + IPC::ResponseBuilder rb{ctx, 2}; + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + return; + } + + SetStereoVisionRestrictionImpl(can_use); + rb.Push(ResultSuccess); + } + + void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & Capability::StereoVision)) { + LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!"); + rb.Push(Error::ResultNoCapability); + rb.Push(false); + return; + } + + rb.Push(ResultSuccess); + rb.Push(settings.is_stero_vision_restricted); + } + + void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + states.stereo_vision = false; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + struct ApplicationInfo { + u64 tid{}; + std::array<u8, 32> age_rating{}; + u32 parental_control_flag{}; + Capability capability{}; + }; + + struct States { + u64 current_tid{}; + ApplicationInfo application_info{}; + u64 tid_from_event{}; + bool launch_time_valid{}; + bool is_suspended{}; + bool temporary_unlocked{}; + bool free_communication{}; + bool stereo_vision{}; + }; + + struct ParentalControlSettings { + bool is_stero_vision_restricted{}; + bool is_free_communication_default_on{}; + bool disabled{}; + }; + + States states{}; + ParentalControlSettings settings{}; + std::array<char, 8> pin_code{}; + Capability capability{}; +}; + +void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + // TODO(ogniK): Get TID from process + + rb.PushIpcInterface<IParentalControlService>(system, capability); +} + +void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface<IParentalControlService>(system, capability); +} + +Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, + const char* name_, Capability capability_) + : ServiceFramework{system_, name_}, module{std::move(module_)}, capability{capability_} {} + +Module::Interface::~Interface() = default; + +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + auto module = std::make_shared<Module>(); + std::make_shared<PCTL>(system, module, "pctl", + Capability::Application | Capability::SnsPost | Capability::Status | + Capability::StereoVision) + ->InstallAsService(service_manager); + // TODO(ogniK): Implement remaining capabilities + std::make_shared<PCTL>(system, module, "pctl:a", Capability::None) + ->InstallAsService(service_manager); + std::make_shared<PCTL>(system, module, "pctl:r", Capability::None) + ->InstallAsService(service_manager); + std::make_shared<PCTL>(system, module, "pctl:s", Capability::None) + ->InstallAsService(service_manager); +} + +} // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/pctl_module.h index f25c5c557..f25c5c557 100644 --- a/src/core/hle/service/pctl/module.h +++ b/src/core/hle/service/pctl/pctl_module.h diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 663b83cd3..b3e50433b 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -21,7 +21,7 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" -#include "core/hle/service/bcat/module.h" +#include "core/hle/service/bcat/bcat_module.h" #include "core/hle/service/bpc/bpc.h" #include "core/hle/service/btdrv/btdrv.h" #include "core/hle/service/btm/btm.h" @@ -54,7 +54,7 @@ #include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/olsc/olsc.h" #include "core/hle/service/pcie/pcie.h" -#include "core/hle/service/pctl/module.h" +#include "core/hle/service/pctl/pctl_module.h" #include "core/hle/service/pcv/pcv.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/prepo/prepo.h" @@ -64,7 +64,7 @@ #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sockets/sockets.h" -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" #include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" #include "core/hle/service/usb/usb.h" @@ -104,23 +104,22 @@ ServiceFrameworkBase::~ServiceFrameworkBase() { void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { const auto guard = LockService(); - ASSERT(!port_installed); + ASSERT(!service_registered); - auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); - port->SetSessionHandler(shared_from_this()); - port_installed = true; + service_manager.RegisterService(service_name, max_sessions, shared_from_this()); + service_registered = true; } Kernel::KClientPort& ServiceFrameworkBase::CreatePort() { const auto guard = LockService(); - ASSERT(!port_installed); + ASSERT(!service_registered); auto* port = Kernel::KPort::Create(kernel); port->Initialize(max_sessions, false, service_name); port->GetServerPort().SetSessionHandler(shared_from_this()); - port_installed = true; + service_registered = true; return port->GetClientPort(); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index e078ac176..c9d6b879d 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -96,6 +96,9 @@ protected: /// System context that the service operates under. Core::System& system; + /// Identifier string used to connect to the service. + std::string service_name; + private: template <typename T> friend class ServiceFramework; @@ -117,14 +120,12 @@ private: void RegisterHandlersBaseTipc(const FunctionInfoBase* functions, std::size_t n); void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); - /// Identifier string used to connect to the service. - std::string service_name; /// Maximum number of concurrent sessions that this service can handle. u32 max_sessions; /// Flag to store if a port was already create/installed to detect multiple install attempts, /// which is not supported. - bool port_installed = false; + bool service_registered = false; /// Function used to safely up-cast pointers to the derived class before invoking a handler. InvokerFn* handler_invoker; diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp deleted file mode 100644 index 8b9418e0f..000000000 --- a/src/core/hle/service/sm/controller.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018 yuzu emulator team -// 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/core.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/k_client_port.h" -#include "core/hle/kernel/k_client_session.h" -#include "core/hle/kernel/k_port.h" -#include "core/hle/kernel/k_scoped_resource_reservation.h" -#include "core/hle/kernel/k_server_port.h" -#include "core/hle/kernel/k_server_session.h" -#include "core/hle/kernel/k_session.h" -#include "core/hle/service/sm/controller.h" - -namespace Service::SM { - -void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { - ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); - LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); - ctx.Session()->ConvertToDomain(); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u32>(1); // Converted sessions start with 1 request handler -} - -void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service, "called"); - - auto& parent_session = *ctx.Session()->GetParent(); - auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); - auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); - - // Create a session. - Kernel::KClientSession* session{}; - const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager); - if (result.IsError()) { - LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - // We succeeded. - IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; - rb.Push(ResultSuccess); - rb.PushMoveObjects(session); -} - -void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service, "called"); - - CloneCurrentObject(ctx); -} - -void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push<u16>(0x8000); -} - -// https://switchbrew.org/wiki/IPC_Marshalling -Controller::Controller(Core::System& system_) : ServiceFramework{system_, "IpcController"} { - static const FunctionInfo functions[] = { - {0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"}, - {1, nullptr, "CopyFromCurrentDomain"}, - {2, &Controller::CloneCurrentObject, "CloneCurrentObject"}, - {3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"}, - {4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"}, - }; - RegisterHandlers(functions); -} - -Controller::~Controller() = default; - -} // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index c7828c3bd..ae4dc4a75 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -4,6 +4,7 @@ #include <tuple> #include "common/assert.h" +#include "common/scope_exit.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_client_port.h" @@ -14,8 +15,8 @@ #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" #include "core/hle/result.h" -#include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/sm_controller.h" namespace Service::SM { @@ -40,17 +41,13 @@ static ResultCode ValidateServiceName(const std::string& name) { } Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core::System& system) { - ASSERT(self.sm_interface.expired()); - - auto sm = std::make_shared<SM>(self, system); - self.sm_interface = sm; + self.sm_interface = std::make_shared<SM>(self, system); self.controller_interface = std::make_unique<Controller>(system); - - return sm->CreatePort(); + return self.sm_interface->CreatePort(); } -ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name, - u32 max_sessions) { +ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler) { CASCADE_CODE(ValidateServiceName(name)); @@ -59,12 +56,9 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name return ERR_ALREADY_REGISTERED; } - auto* port = Kernel::KPort::Create(kernel); - port->Initialize(max_sessions, false, name); + registered_services.emplace(std::move(name), handler); - registered_services.emplace(std::move(name), port); - - return MakeResult(&port->GetServerPort()); + return ResultSuccess; } ResultCode ServiceManager::UnregisterService(const std::string& name) { @@ -76,14 +70,11 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) { return ERR_SERVICE_NOT_REGISTERED; } - iter->second->Close(); - registered_services.erase(iter); return ResultSuccess; } ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { - CASCADE_CODE(ValidateServiceName(name)); auto it = registered_services.find(name); if (it == registered_services.end()) { @@ -91,10 +82,13 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name return ERR_SERVICE_NOT_REGISTERED; } - return MakeResult(it->second); -} + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, false, name); + auto handler = it->second; + port->GetServerPort().SetSessionHandler(std::move(handler)); -SM::~SM() = default; + return MakeResult(port); +} /** * SM::Initialize service function @@ -156,11 +150,15 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext& LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw); return port_result.Code(); } - auto& port = port_result.Unwrap()->GetClientPort(); + auto& port = port_result.Unwrap(); + SCOPE_EXIT({ port->GetClientPort().Close(); }); + + server_ports.emplace_back(&port->GetServerPort()); // Create a new session. Kernel::KClientSession* session{}; - if (const auto result = port.CreateSession(std::addressof(session)); result.IsError()) { + if (const auto result = port->GetClientPort().CreateSession(std::addressof(session)); + result.IsError()) { LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw); return result; } @@ -180,20 +178,21 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, max_session_count, is_light); - auto handle = service_manager.RegisterService(name, max_session_count); - if (handle.Failed()) { - LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", - handle.Code().raw); + if (const auto result = service_manager.RegisterService(name, max_session_count, nullptr); + result.IsError()) { + LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(handle.Code()); + rb.Push(result); return; } - IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; - rb.Push(handle.Code()); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(ServerSessionCountMax, is_light, name); + SCOPE_EXIT({ port->GetClientPort().Close(); }); - auto server_port = handle.Unwrap(); - rb.PushMoveObjects(server_port); + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; + rb.Push(ResultSuccess); + rb.PushMoveObjects(port->GetServerPort()); } void SM::UnregisterService(Kernel::HLERequestContext& ctx) { @@ -225,4 +224,10 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_) }); } +SM::~SM() { + for (auto& server_port : server_ports) { + server_port->Close(); + } +} + } // namespace Service::SM diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index ea37f11d4..068c78588 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -49,6 +49,7 @@ private: ServiceManager& service_manager; bool is_initialized{}; Kernel::KernelCore& kernel; + std::vector<Kernel::KServerPort*> server_ports; }; class ServiceManager { @@ -58,7 +59,8 @@ public: explicit ServiceManager(Kernel::KernelCore& kernel_); ~ServiceManager(); - ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions); + ResultCode RegisterService(std::string name, u32 max_sessions, + Kernel::SessionRequestHandlerPtr handler); ResultCode UnregisterService(const std::string& name); ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); @@ -69,21 +71,17 @@ public: LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; } - auto* port = service->second; - if (port == nullptr) { - return nullptr; - } - return std::static_pointer_cast<T>(port->GetServerPort().GetSessionRequestHandler()); + return std::static_pointer_cast<T>(service->second); } void InvokeControlRequest(Kernel::HLERequestContext& context); private: - std::weak_ptr<SM> sm_interface; + std::shared_ptr<SM> sm_interface; std::unique_ptr<Controller> controller_interface; /// Map of registered services, retrieved using GetServicePort. - std::unordered_map<std::string, Kernel::KPort*> registered_services; + std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services; /// Kernel context Kernel::KernelCore& kernel; diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp new file mode 100644 index 000000000..b5fbc4569 --- /dev/null +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -0,0 +1,80 @@ +// Copyright 2018 yuzu emulator team +// 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/core.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_client_session.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" +#include "core/hle/kernel/k_server_port.h" +#include "core/hle/kernel/k_server_session.h" +#include "core/hle/kernel/k_session.h" +#include "core/hle/service/sm/sm_controller.h" + +namespace Service::SM { + +void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { + ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); + LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); + ctx.Session()->ConvertToDomain(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u32>(1); // Converted sessions start with 1 request handler +} + +void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service, "called"); + + auto& parent_session = *ctx.Session()->GetParent(); + auto& parent_port = parent_session.GetParent()->GetParent()->GetClientPort(); + auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager(); + + // Create a session. + Kernel::KClientSession* session{}; + const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager); + if (result.IsError()) { + LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + // We succeeded. + IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; + rb.Push(ResultSuccess); + rb.PushMoveObjects(session); +} + +void Controller::CloneCurrentObjectEx(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service, "called"); + + CloneCurrentObject(ctx); +} + +void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push<u16>(0x8000); +} + +// https://switchbrew.org/wiki/IPC_Marshalling +Controller::Controller(Core::System& system_) : ServiceFramework{system_, "IpcController"} { + static const FunctionInfo functions[] = { + {0, &Controller::ConvertCurrentObjectToDomain, "ConvertCurrentObjectToDomain"}, + {1, nullptr, "CopyFromCurrentDomain"}, + {2, &Controller::CloneCurrentObject, "CloneCurrentObject"}, + {3, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"}, + {4, &Controller::CloneCurrentObjectEx, "CloneCurrentObjectEx"}, + }; + RegisterHandlers(functions); +} + +Controller::~Controller() = default; + +} // namespace Service::SM diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/sm_controller.h index 7494f898d..7494f898d 100644 --- a/src/core/hle/service/sm/controller.h +++ b/src/core/hle/service/sm/sm_controller.h diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h index 5c0bd2199..0d03cc6cb 100644 --- a/src/core/hle/service/spl/csrng.h +++ b/src/core/hle/service/spl/csrng.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp deleted file mode 100644 index ebb179aa8..000000000 --- a/src/core/hle/service/spl/module.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <chrono> -#include <cstdlib> -#include <ctime> -#include <functional> -#include <vector> -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/hle/api_version.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/spl/csrng.h" -#include "core/hle/service/spl/module.h" -#include "core/hle/service/spl/spl.h" - -namespace Service::SPL { - -Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)}, - rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {} - -Module::Interface::~Interface() = default; - -void Module::Interface::GetConfig(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto config_item = rp.PopEnum<ConfigItem>(); - - // This should call svcCallSecureMonitor with the appropriate args. - // Since we do not have it implemented yet, we will use this for now. - const auto smc_result = GetConfigImpl(config_item); - const auto result_code = smc_result.Code(); - - if (smc_result.Failed()) { - LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item, - result_code.raw); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result_code); - } - - LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item, - result_code.raw, *smc_result); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result_code); - rb.Push(*smc_result); -} - -void Module::Interface::ModularExponentiate(Kernel::HLERequestContext& ctx) { - UNIMPLEMENTED_MSG("ModularExponentiate is not implemented!"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSecureMonitorNotImplemented); -} - -void Module::Interface::SetConfig(Kernel::HLERequestContext& ctx) { - UNIMPLEMENTED_MSG("SetConfig is not implemented!"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSecureMonitorNotImplemented); -} - -void Module::Interface::GenerateRandomBytes(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_SPL, "called"); - - const std::size_t size = ctx.GetWriteBufferSize(); - - std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); - std::vector<u8> data(size); - std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); }); - - ctx.WriteBuffer(data); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Module::Interface::IsDevelopment(Kernel::HLERequestContext& ctx) { - UNIMPLEMENTED_MSG("IsDevelopment is not implemented!"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSecureMonitorNotImplemented); -} - -void Module::Interface::SetBootReason(Kernel::HLERequestContext& ctx) { - UNIMPLEMENTED_MSG("SetBootReason is not implemented!"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSecureMonitorNotImplemented); -} - -void Module::Interface::GetBootReason(Kernel::HLERequestContext& ctx) { - UNIMPLEMENTED_MSG("GetBootReason is not implemented!"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSecureMonitorNotImplemented); -} - -ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const { - switch (config_item) { - case ConfigItem::DisableProgramVerification: - case ConfigItem::DramId: - case ConfigItem::SecurityEngineInterruptNumber: - case ConfigItem::FuseVersion: - case ConfigItem::HardwareType: - case ConfigItem::HardwareState: - case ConfigItem::IsRecoveryBoot: - case ConfigItem::DeviceId: - case ConfigItem::BootReason: - case ConfigItem::MemoryMode: - case ConfigItem::IsDevelopmentFunctionEnabled: - case ConfigItem::KernelConfiguration: - case ConfigItem::IsChargerHiZModeEnabled: - case ConfigItem::QuestState: - case ConfigItem::RegulatorType: - case ConfigItem::DeviceUniqueKeyGeneration: - case ConfigItem::Package2Hash: - return ResultSecureMonitorNotImplemented; - case ConfigItem::ExosphereApiVersion: - // Get information about the current exosphere version. - return MakeResult((u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) | - (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) | - (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) | - (static_cast<u64>(HLE::ApiVersion::GetTargetFirmware()))); - case ConfigItem::ExosphereNeedsReboot: - // We are executing, so we aren't in the process of rebooting. - return MakeResult(u64{0}); - case ConfigItem::ExosphereNeedsShutdown: - // We are executing, so we aren't in the process of shutting down. - return MakeResult(u64{0}); - case ConfigItem::ExosphereGitCommitHash: - // Get information about the current exosphere git commit hash. - return MakeResult(u64{0}); - case ConfigItem::ExosphereHasRcmBugPatch: - // Get information about whether this unit has the RCM bug patched. - return MakeResult(u64{0}); - case ConfigItem::ExosphereBlankProdInfo: - // Get whether this unit should simulate a "blanked" PRODINFO. - return MakeResult(u64{0}); - case ConfigItem::ExosphereAllowCalWrites: - // Get whether this unit should allow writing to the calibration partition. - return MakeResult(u64{0}); - case ConfigItem::ExosphereEmummcType: - // Get what kind of emummc this unit has active. - return MakeResult(u64{0}); - case ConfigItem::ExospherePayloadAddress: - // Gets the physical address of the reboot payload buffer, if one exists. - return ResultSecureMonitorNotInitialized; - case ConfigItem::ExosphereLogConfiguration: - // Get the log configuration. - return MakeResult(u64{0}); - case ConfigItem::ExosphereForceEnableUsb30: - // Get whether usb 3.0 should be force-enabled. - return MakeResult(u64{0}); - default: - return ResultSecureMonitorInvalidArgument; - } -} - -void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { - auto module = std::make_shared<Module>(); - std::make_shared<CSRNG>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL_MIG>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL_FS>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL_SSL>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL_ES>(system, module)->InstallAsService(service_manager); - std::make_shared<SPL_MANU>(system, module)->InstallAsService(service_manager); -} - -} // namespace Service::SPL diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h index 9b35012ed..5599c0c01 100644 --- a/src/core/hle/service/spl/spl.h +++ b/src/core/hle/service/spl/spl.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/service/spl/module.h" +#include "core/hle/service/spl/spl_module.h" namespace Core { class System; diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp new file mode 100644 index 000000000..918633af5 --- /dev/null +++ b/src/core/hle/service/spl/spl_module.cpp @@ -0,0 +1,175 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <chrono> +#include <cstdlib> +#include <ctime> +#include <functional> +#include <vector> +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/hle/api_version.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/spl/csrng.h" +#include "core/hle/service/spl/spl.h" +#include "core/hle/service/spl/spl_module.h" + +namespace Service::SPL { + +Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, + const char* name) + : ServiceFramework{system_, name}, module{std::move(module_)}, + rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr))) {} + +Module::Interface::~Interface() = default; + +void Module::Interface::GetConfig(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto config_item = rp.PopEnum<ConfigItem>(); + + // This should call svcCallSecureMonitor with the appropriate args. + // Since we do not have it implemented yet, we will use this for now. + const auto smc_result = GetConfigImpl(config_item); + const auto result_code = smc_result.Code(); + + if (smc_result.Failed()) { + LOG_ERROR(Service_SPL, "called, config_item={}, result_code={}", config_item, + result_code.raw); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result_code); + } + + LOG_DEBUG(Service_SPL, "called, config_item={}, result_code={}, smc_result={}", config_item, + result_code.raw, *smc_result); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result_code); + rb.Push(*smc_result); +} + +void Module::Interface::ModularExponentiate(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED_MSG("ModularExponentiate is not implemented!"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSecureMonitorNotImplemented); +} + +void Module::Interface::SetConfig(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED_MSG("SetConfig is not implemented!"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSecureMonitorNotImplemented); +} + +void Module::Interface::GenerateRandomBytes(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SPL, "called"); + + const std::size_t size = ctx.GetWriteBufferSize(); + + std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max()); + std::vector<u8> data(size); + std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); }); + + ctx.WriteBuffer(data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void Module::Interface::IsDevelopment(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED_MSG("IsDevelopment is not implemented!"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSecureMonitorNotImplemented); +} + +void Module::Interface::SetBootReason(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED_MSG("SetBootReason is not implemented!"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSecureMonitorNotImplemented); +} + +void Module::Interface::GetBootReason(Kernel::HLERequestContext& ctx) { + UNIMPLEMENTED_MSG("GetBootReason is not implemented!"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSecureMonitorNotImplemented); +} + +ResultVal<u64> Module::Interface::GetConfigImpl(ConfigItem config_item) const { + switch (config_item) { + case ConfigItem::DisableProgramVerification: + case ConfigItem::DramId: + case ConfigItem::SecurityEngineInterruptNumber: + case ConfigItem::FuseVersion: + case ConfigItem::HardwareType: + case ConfigItem::HardwareState: + case ConfigItem::IsRecoveryBoot: + case ConfigItem::DeviceId: + case ConfigItem::BootReason: + case ConfigItem::MemoryMode: + case ConfigItem::IsDevelopmentFunctionEnabled: + case ConfigItem::KernelConfiguration: + case ConfigItem::IsChargerHiZModeEnabled: + case ConfigItem::QuestState: + case ConfigItem::RegulatorType: + case ConfigItem::DeviceUniqueKeyGeneration: + case ConfigItem::Package2Hash: + return ResultSecureMonitorNotImplemented; + case ConfigItem::ExosphereApiVersion: + // Get information about the current exosphere version. + return MakeResult((u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MAJOR} << 56) | + (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MINOR} << 48) | + (u64{HLE::ApiVersion::ATMOSPHERE_RELEASE_VERSION_MICRO} << 40) | + (static_cast<u64>(HLE::ApiVersion::GetTargetFirmware()))); + case ConfigItem::ExosphereNeedsReboot: + // We are executing, so we aren't in the process of rebooting. + return MakeResult(u64{0}); + case ConfigItem::ExosphereNeedsShutdown: + // We are executing, so we aren't in the process of shutting down. + return MakeResult(u64{0}); + case ConfigItem::ExosphereGitCommitHash: + // Get information about the current exosphere git commit hash. + return MakeResult(u64{0}); + case ConfigItem::ExosphereHasRcmBugPatch: + // Get information about whether this unit has the RCM bug patched. + return MakeResult(u64{0}); + case ConfigItem::ExosphereBlankProdInfo: + // Get whether this unit should simulate a "blanked" PRODINFO. + return MakeResult(u64{0}); + case ConfigItem::ExosphereAllowCalWrites: + // Get whether this unit should allow writing to the calibration partition. + return MakeResult(u64{0}); + case ConfigItem::ExosphereEmummcType: + // Get what kind of emummc this unit has active. + return MakeResult(u64{0}); + case ConfigItem::ExospherePayloadAddress: + // Gets the physical address of the reboot payload buffer, if one exists. + return ResultSecureMonitorNotInitialized; + case ConfigItem::ExosphereLogConfiguration: + // Get the log configuration. + return MakeResult(u64{0}); + case ConfigItem::ExosphereForceEnableUsb30: + // Get whether usb 3.0 should be force-enabled. + return MakeResult(u64{0}); + default: + return ResultSecureMonitorInvalidArgument; + } +} + +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + auto module = std::make_shared<Module>(); + std::make_shared<CSRNG>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL_MIG>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL_FS>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL_SSL>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL_ES>(system, module)->InstallAsService(service_manager); + std::make_shared<SPL_MANU>(system, module)->InstallAsService(service_manager); +} + +} // namespace Service::SPL diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/spl_module.h index 61630df80..61630df80 100644 --- a/src/core/hle/service/spl/module.h +++ b/src/core/hle/service/spl/spl_module.h diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp deleted file mode 100644 index 53a204796..000000000 --- a/src/core/hle/service/time/interface.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/time/interface.h" - -namespace Service::Time { - -Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_) - : Interface{std::move(module_), system_, name_} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, - {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, - {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, - {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, - {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, - {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, - {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, - {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, - {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {51, nullptr, "GetStandardSteadyClockRtcValue"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, - {102, nullptr, "GetStandardUserSystemClockInitialYear"}, - {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, - {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, - {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, - {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, - {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, - {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, - {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Time::~Time() = default; - -} // namespace Service::Time diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index d6f710eba..8fdd5076f 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -11,8 +11,8 @@ #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/service/time/interface.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_interface.h" #include "core/hle/service/time/time_sharedmemory.h" #include "core/hle/service/time/time_zone_service.h" diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp new file mode 100644 index 000000000..bb7b6b5c1 --- /dev/null +++ b/src/core/hle/service/time/time_interface.cpp @@ -0,0 +1,42 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/time/time_interface.h" + +namespace Service::Time { + +Time::Time(std::shared_ptr<Module> module_, Core::System& system_, const char* name_) + : Interface{std::move(module_), system_, name_} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, + {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"}, + {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"}, + {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, + {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, + {5, nullptr, "GetEphemeralNetworkSystemClock"}, + {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, + {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, + {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, + {50, nullptr, "SetStandardSteadyClockInternalOffset"}, + {51, nullptr, "GetStandardSteadyClockRtcValue"}, + {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {102, nullptr, "GetStandardUserSystemClockInitialYear"}, + {200, &Time::IsStandardNetworkSystemClockAccuracySufficient, "IsStandardNetworkSystemClockAccuracySufficient"}, + {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, + {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"}, + {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, + {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, + {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"}, + {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +Time::~Time() = default; + +} // namespace Service::Time diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/time_interface.h index c41766f1a..c41766f1a 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/time_interface.h diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 228dc6389..199e69e89 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -206,7 +206,8 @@ AppLoader::~AppLoader() = default; * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type */ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, - FileType type, std::size_t program_index) { + FileType type, u64 program_id, + std::size_t program_index) { switch (type) { // Standard ELF file format. case FileType::ELF: @@ -227,7 +228,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V // NX XCI (nX Card Image) file format. case FileType::XCI: return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), - system.GetContentProvider(), program_index); + system.GetContentProvider(), program_id, + program_index); // NX NAX (NintendoAesXts) file format. case FileType::NAX: @@ -236,7 +238,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V // NX NSP (Nintendo Submission Package) file format case FileType::NSP: return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), - system.GetContentProvider(), program_index); + system.GetContentProvider(), program_id, + program_index); // NX KIP (Kernel Internal Process) file format case FileType::KIP: @@ -252,7 +255,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V } std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, - std::size_t program_index) { + u64 program_id, std::size_t program_index) { FileType type = IdentifyFile(file); const FileType filename_type = GuessFromFilename(file->GetName()); @@ -266,7 +269,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); - return GetFileLoader(system, std::move(file), type, program_index); + return GetFileLoader(system, std::move(file), type, program_id, program_index); } } // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index edc8bb257..7b1bac3f7 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -227,6 +227,17 @@ public: } /** + * Get the program ids of the application + * + * @param[out] out_program_ids Reference to store program ids into + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) { + return ResultStatus::ErrorNotImplemented; + } + + /** * Get the RomFS of the application * Since the RomFS can be huge, we return a file reference instead of copying to a buffer * @@ -324,6 +335,6 @@ protected: * @return the best loader for this file. */ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, - std::size_t program_index = 0); + u64 program_id = 0, std::size_t program_index = 0); } // namespace Loader diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index d815a7cd3..8b167ad3c 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -23,10 +23,9 @@ namespace Loader { AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, const Service::FileSystem::FileSystemController& fsc, - const FileSys::ContentProvider& content_provider, + const FileSys::ContentProvider& content_provider, u64 program_id, std::size_t program_index) - : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_index)), - title_id(nsp->GetProgramTitleID()) { + : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) { if (nsp->GetStatus() != ResultStatus::Success) { return; @@ -46,12 +45,8 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, return pm.ParseControlNCA(*control_nca); }(); - if (title_id == 0) { - return; - } - secondary_loader = std::make_unique<AppLoader_NCA>( - nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program)); } } @@ -68,10 +63,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) { } // Non-Extracted Type case + const auto program_id = nsp.GetProgramTitleID(); if (!nsp.IsExtractedType() && - nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && - AppLoader_NCA::IdentifyType(nsp.GetNCAFile( - nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) { + nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr && + AppLoader_NCA::IdentifyType( + nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) { return FileType::NSP; } } @@ -84,6 +80,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return {ResultStatus::ErrorAlreadyLoaded, {}}; } + const auto title_id = nsp->GetProgramTitleID(); + if (!nsp->IsExtractedType() && title_id == 0) { return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; } @@ -93,7 +91,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S return {nsp_status, {}}; } - const auto nsp_program_status = nsp->GetProgramStatus(title_id); + const auto nsp_program_status = nsp->GetProgramStatus(); if (nsp_program_status != ResultStatus::Success) { return {nsp_program_status, {}}; } @@ -134,8 +132,8 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) { return ResultStatus::ErrorNoPackedUpdate; } - const auto read = - nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program); + const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()), + FileSys::ContentRecordType::Program); if (read == nullptr) { return ResultStatus::ErrorNoPackedUpdate; @@ -151,11 +149,15 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) { } ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { - if (title_id == 0) { + out_program_id = nsp->GetProgramTitleID(); + if (out_program_id == 0) { return ResultStatus::ErrorNotInitialized; } + return ResultStatus::Success; +} - out_program_id = title_id; +ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) { + out_program_ids = nsp->GetProgramTitleIDs(); return ResultStatus::Success; } diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 644c0ff58..50406a92e 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -28,7 +28,7 @@ class AppLoader_NSP final : public AppLoader { public: explicit AppLoader_NSP(FileSys::VirtualFile file_, const Service::FileSystem::FileSystemController& fsc, - const FileSys::ContentProvider& content_provider, + const FileSys::ContentProvider& content_provider, u64 program_id, std::size_t program_index); ~AppLoader_NSP() override; @@ -51,6 +51,7 @@ public: u64 ReadRomFSIVFCOffset() const override; ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; ResultStatus ReadControlData(FileSys::NACP& nacp) override; @@ -67,7 +68,6 @@ private: FileSys::VirtualFile icon_file; std::unique_ptr<FileSys::NACP> nacp_file; - u64 title_id; }; } // namespace Loader diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 635d6ae15..269603eef 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -22,9 +22,9 @@ namespace Loader { AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_, const Service::FileSystem::FileSystemController& fsc, - const FileSys::ContentProvider& content_provider, + const FileSys::ContentProvider& content_provider, u64 program_id, std::size_t program_index) - : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_index)), + : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)), nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { if (xci->GetStatus() != ResultStatus::Success) { return; @@ -121,6 +121,11 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { return nca_loader->ReadProgramId(out_program_id); } +ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) { + out_program_ids = xci->GetProgramTitleIDs(); + return ResultStatus::Success; +} + ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { if (icon_file == nullptr) { return ResultStatus::ErrorNoControl; @@ -149,8 +154,9 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) { } ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) { - const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(), - FileSys::ContentRecordType::HtmlDocument); + const auto nca = + xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(), + FileSys::ContentRecordType::HtmlDocument); if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) { return ResultStatus::ErrorXCIMissingPartition; } diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 708155c30..30caaf90e 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h @@ -28,7 +28,7 @@ class AppLoader_XCI final : public AppLoader { public: explicit AppLoader_XCI(FileSys::VirtualFile file_, const Service::FileSystem::FileSystemController& fsc, - const FileSys::ContentProvider& content_provider, + const FileSys::ContentProvider& content_provider, u64 program_id, std::size_t program_index); ~AppLoader_XCI() override; @@ -51,6 +51,7 @@ public: u64 ReadRomFSIVFCOffset() const override; ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadTitle(std::string& title) override; ResultStatus ReadControlData(FileSys::NACP& control) override; diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index cfaf50105..365b8f906 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -62,7 +62,6 @@ json GetYuzuVersionData() { {"build_date", std::string(Common::g_build_date)}, {"build_fullname", std::string(Common::g_build_fullname)}, {"build_version", std::string(Common::g_build_version)}, - {"shader_cache_version", std::string(Common::g_shader_cache_version)}, }; } diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 066cb23e4..422de3a7d 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -233,8 +233,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, Settings::values.use_nvdec_emulation.GetValue()); AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue()); AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); - AddField(field_type, "Renderer_UseAssemblyShaders", - Settings::values.use_assembly_shaders.GetValue()); + AddField(field_type, "Renderer_ShaderBackend", + static_cast<u32>(Settings::values.shader_backend.GetValue())); AddField(field_type, "Renderer_UseAsynchronousShaders", Settings::values.use_asynchronous_shaders.GetValue()); AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue()); |