diff options
Diffstat (limited to '')
160 files changed, 4900 insertions, 1129 deletions
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index cb66e344b..0c0f7ed6e 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -12,16 +12,28 @@ #include "common/swap.h" #include "core/constants.h" #include "core/core_timing.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/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/acc/acc_aa.h" #include "core/hle/service/acc/acc_su.h" #include "core/hle/service/acc/acc_u0.h" #include "core/hle/service/acc/acc_u1.h" +#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/sm/sm.h" +#include "core/loader/loader.h" namespace Service::Account { +constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30}; +constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; + static std::string GetImagePath(Common::UUID uuid) { return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; @@ -32,22 +44,33 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) { return static_cast<u32>(std::min(size, max_jpeg_image_size)); } -class IProfile final : public ServiceFramework<IProfile> { +class IProfileCommon : public ServiceFramework<IProfileCommon> { public: - explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager) - : ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) { + explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id, + ProfileManager& profile_manager) + : ServiceFramework(name), profile_manager(profile_manager), user_id(user_id) { static const FunctionInfo functions[] = { - {0, &IProfile::Get, "Get"}, - {1, &IProfile::GetBase, "GetBase"}, - {10, &IProfile::GetImageSize, "GetImageSize"}, - {11, &IProfile::LoadImage, "LoadImage"}, + {0, &IProfileCommon::Get, "Get"}, + {1, &IProfileCommon::GetBase, "GetBase"}, + {10, &IProfileCommon::GetImageSize, "GetImageSize"}, + {11, &IProfileCommon::LoadImage, "LoadImage"}, }; + RegisterHandlers(functions); + + if (editor_commands) { + static const FunctionInfo editor_functions[] = { + {100, &IProfileCommon::Store, "Store"}, + {101, &IProfileCommon::StoreWithImage, "StoreWithImage"}, + }; + + RegisterHandlers(editor_functions); + } } -private: +protected: void Get(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); + LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; ProfileData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { @@ -66,7 +89,7 @@ private: } void GetBase(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); + LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); ProfileBase profile_base{}; if (profile_manager.GetProfileBase(user_id, profile_base)) { IPC::ResponseBuilder rb{ctx, 16}; @@ -90,7 +113,7 @@ private: LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); - rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()); + rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); return; } @@ -112,16 +135,97 @@ private: if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); - rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()); + rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); } else { - rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); + rb.Push(SanitizeJPEGSize(image.GetSize())); + } + } + + void Store(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto base = rp.PopRaw<ProfileBase>(); + + const auto user_data = ctx.ReadBuffer(); + + LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", + Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(base.username.data()), base.username.size()), + base.timestamp, base.user_uuid.Format()); + + if (user_data.size() < sizeof(ProfileData)) { + LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_BUFFER_SIZE); + return; + } + + ProfileData data; + std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + + if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) { + LOG_ERROR(Service_ACC, "Failed to update profile data and base!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_FAILED_SAVE_DATA); + return; } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); } - const ProfileManager& profile_manager; + void StoreWithImage(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto base = rp.PopRaw<ProfileBase>(); + + const auto user_data = ctx.ReadBuffer(); + const auto image_data = ctx.ReadBuffer(1); + + LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}", + Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(base.username.data()), base.username.size()), + base.timestamp, base.user_uuid.Format()); + + if (user_data.size() < sizeof(ProfileData)) { + LOG_ERROR(Service_ACC, "ProfileData buffer too small!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_BUFFER_SIZE); + return; + } + + ProfileData data; + std::memcpy(&data, user_data.data(), sizeof(ProfileData)); + + FileUtil::IOFile image(GetImagePath(user_id), "wb"); + + if (!image.IsOpen() || !image.Resize(image_data.size()) || + image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || + !profile_manager.SetProfileBaseAndData(user_id, base, data)) { + LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_FAILED_SAVE_DATA); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + ProfileManager& profile_manager; Common::UUID user_id; ///< The user id this profile refers to. }; +class IProfile final : public IProfileCommon { +public: + IProfile(Common::UUID user_id, ProfileManager& profile_manager) + : IProfileCommon("IProfile", false, user_id, profile_manager) {} +}; + +class IProfileEditor final : public IProfileCommon { +public: + IProfileEditor(Common::UUID user_id, ProfileManager& profile_manager) + : IProfileCommon("IProfileEditor", true, user_id, profile_manager) {} +}; + class IManagerForApplication final : public ServiceFramework<IManagerForApplication> { public: IManagerForApplication() : ServiceFramework("IManagerForApplication") { @@ -159,7 +263,7 @@ private: }; void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); @@ -168,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; Common::UUID user_id = rp.PopRaw<Common::UUID>(); - LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); + LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format()); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -176,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { } void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); ctx.WriteBuffer(profile_manager->GetAllUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); ctx.WriteBuffer(profile_manager->GetOpenUsers()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_ACC, "called"); + LOG_DEBUG(Service_ACC, "called"); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); @@ -214,9 +318,71 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon } void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_ACC, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto pid = rp.Pop<u64>(); + + LOG_DEBUG(Service_ACC, "called, process_id={}", pid); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + rb.Push(InitializeApplicationInfoBase(pid)); +} + +void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto pid = rp.Pop<u64>(); + + LOG_WARNING(Service_ACC, "(Partial implementation) called, process_id={}", pid); + + // TODO(ogniK): We require checking if the user actually owns the title and what not. As of + // currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called + // first then we do extra checks if the game is a digital copy. + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InitializeApplicationInfoBase(pid)); +} + +ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) { + if (application_info) { + LOG_ERROR(Service_ACC, "Application already initialized"); + return ERR_ACCOUNTINFO_ALREADY_INITIALIZED; + } + + const auto& list = system.Kernel().GetProcessList(); + const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { + return process->GetProcessID() == process_id; + }); + + if (iter == list.end()) { + LOG_ERROR(Service_ACC, "Failed to find process ID"); + application_info.application_type = ApplicationType::Unknown; + + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + const auto launch_property = system.GetARPManager().GetLaunchProperty((*iter)->GetTitleID()); + + if (launch_property.Failed()) { + LOG_ERROR(Service_ACC, "Failed to get launch property"); + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + switch (launch_property->base_game_storage_id) { + case FileSys::StorageId::GameCard: + application_info.application_type = ApplicationType::GameCard; + break; + case FileSys::StorageId::Host: + case FileSys::StorageId::NandUser: + case FileSys::StorageId::SdCard: + application_info.application_type = ApplicationType::Digital; + break; + default: + LOG_ERROR(Service_ACC, "Invalid game storage ID"); + return ERR_ACCOUNTINFO_BAD_APPLICATION; + } + + LOG_WARNING(Service_ACC, "ApplicationInfo init required"); + // TODO(ogniK): Actual initalization here + + return RESULT_SUCCESS; } void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { @@ -226,6 +392,42 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo rb.PushIpcInterface<IManagerForApplication>(); } +void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + FileSys::NACP nacp; + const auto res = system.GetAppLoader().ReadControlData(nacp); + + bool is_locked = false; + + if (res != Loader::ResultStatus::Success) { + FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; + auto nacp_unique = pm.GetControlMetadata().first; + + if (nacp_unique != nullptr) { + is_locked = nacp_unique->GetUserAccountSwitchLock(); + } else { + LOG_ERROR(Service_ACC, "nacp_unique is null!"); + } + } else { + is_locked = nacp.GetUserAccountSwitchLock(); + } + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(is_locked); +} + +void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + Common::UUID user_id = rp.PopRaw<Common::UUID>(); + + LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IProfileEditor>(user_id, *profile_manager); +} + void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); // A u8 is passed into this function which we can safely ignore. It's to determine if we have @@ -251,19 +453,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex } Module::Interface::Interface(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager, const char* name) + std::shared_ptr<ProfileManager> profile_manager, Core::System& system, + const char* name) : ServiceFramework(name), module(std::move(module)), - profile_manager(std::move(profile_manager)) {} + profile_manager(std::move(profile_manager)), system(system) {} Module::Interface::~Interface() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto module = std::make_shared<Module>(); auto profile_manager = std::make_shared<ProfileManager>(); - std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager); - std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager); - std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager); - std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager); + + std::make_shared<ACC_AA>(module, profile_manager, system) + ->InstallAsService(system.ServiceManager()); + std::make_shared<ACC_SU>(module, profile_manager, system) + ->InstallAsService(system.ServiceManager()); + std::make_shared<ACC_U0>(module, profile_manager, system) + ->InstallAsService(system.ServiceManager()); + std::make_shared<ACC_U1>(module, profile_manager, system) + ->InstallAsService(system.ServiceManager()); } } // namespace Service::Account diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index 89b2104fa..7a7dc9ec6 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h @@ -4,6 +4,7 @@ #pragma once +#include "core/hle/service/glue/manager.h" #include "core/hle/service/service.h" namespace Service::Account { @@ -15,7 +16,8 @@ public: class Interface : public ServiceFramework<Interface> { public: explicit Interface(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager, const char* name); + std::shared_ptr<ProfileManager> profile_manager, Core::System& system, + const char* name); ~Interface() override; void GetUserCount(Kernel::HLERequestContext& ctx); @@ -25,17 +27,41 @@ public: void GetLastOpenedUser(Kernel::HLERequestContext& ctx); void GetProfile(Kernel::HLERequestContext& ctx); void InitializeApplicationInfo(Kernel::HLERequestContext& ctx); + void InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx); void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx); + void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); + void GetProfileEditor(Kernel::HLERequestContext& ctx); + + private: + ResultCode InitializeApplicationInfoBase(u64 process_id); + + enum class ApplicationType : u32_le { + GameCard = 0, + Digital = 1, + Unknown = 3, + }; + + struct ApplicationInfo { + Service::Glue::ApplicationLaunchProperty launch_property; + ApplicationType application_type; + + constexpr explicit operator bool() const { + return launch_property.title_id != 0x0; + } + }; + + ApplicationInfo application_info{}; protected: std::shared_ptr<Module> module; std::shared_ptr<ProfileManager> profile_manager; + Core::System& system; }; }; /// Registers all ACC services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::Account diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp index e84d9f7cf..3bac6bcd1 100644 --- a/src/core/hle/service/acc/acc_aa.cpp +++ b/src/core/hle/service/acc/acc_aa.cpp @@ -6,8 +6,9 @@ namespace Service::Account { -ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) - : Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") { +ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system) + : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:aa") { static const FunctionInfo functions[] = { {0, nullptr, "EnsureCacheAsync"}, {1, nullptr, "LoadCache"}, diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h index 9edb0421b..932c04890 100644 --- a/src/core/hle/service/acc/acc_aa.h +++ b/src/core/hle/service/acc/acc_aa.h @@ -10,8 +10,8 @@ namespace Service::Account { class ACC_AA final : public Module::Interface { public: - explicit ACC_AA(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager); + explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system); ~ACC_AA() override; }; diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp index d66233cad..0d1663657 100644 --- a/src/core/hle/service/acc/acc_su.cpp +++ b/src/core/hle/service/acc/acc_su.cpp @@ -6,8 +6,9 @@ namespace Service::Account { -ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) - : Module::Interface(std::move(module), std::move(profile_manager), "acc:su") { +ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system) + : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_SU::GetUserCount, "GetUserCount"}, @@ -40,7 +41,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {202, nullptr, "CancelUserRegistration"}, {203, nullptr, "DeleteUser"}, {204, nullptr, "SetUserPosition"}, - {205, nullptr, "GetProfileEditor"}, + {205, &ACC_SU::GetProfileEditor, "GetProfileEditor"}, {206, nullptr, "CompleteUserRegistrationForcibly"}, {210, nullptr, "CreateFloatingRegistrationRequest"}, {230, nullptr, "AuthenticateServiceAsync"}, diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h index fcced063a..0a700d9bf 100644 --- a/src/core/hle/service/acc/acc_su.h +++ b/src/core/hle/service/acc/acc_su.h @@ -10,8 +10,8 @@ namespace Service::Account { class ACC_SU final : public Module::Interface { public: - explicit ACC_SU(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager); + explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system); ~ACC_SU() override; }; diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index 182f7c7e5..0ac19f4ff 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp @@ -6,8 +6,9 @@ namespace Service::Account { -ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) - : Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") { +ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system) + : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U0::GetUserCount, "GetUserCount"}, @@ -30,9 +31,9 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p {120, nullptr, "CreateGuestLoginRequest"}, {130, nullptr, "LoadOpenContext"}, {131, nullptr, "ListOpenContextStoredUsers"}, - {140, nullptr, "InitializeApplicationInfo"}, + {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, {141, nullptr, "ListQualifiedUsers"}, - {150, nullptr, "IsUserAccountSwitchLocked"}, + {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, }; // clang-format on diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h index a1290e0bd..3bd9c3164 100644 --- a/src/core/hle/service/acc/acc_u0.h +++ b/src/core/hle/service/acc/acc_u0.h @@ -10,8 +10,8 @@ namespace Service::Account { class ACC_U0 final : public Module::Interface { public: - explicit ACC_U0(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager); + explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system); ~ACC_U0() override; }; diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp index 2dd17d935..6520b3968 100644 --- a/src/core/hle/service/acc/acc_u1.cpp +++ b/src/core/hle/service/acc/acc_u1.cpp @@ -6,8 +6,9 @@ namespace Service::Account { -ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager) - : Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") { +ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system) + : Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") { // clang-format off static const FunctionInfo functions[] = { {0, &ACC_U1::GetUserCount, "GetUserCount"}, diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h index 9e79daee3..829f8a744 100644 --- a/src/core/hle/service/acc/acc_u1.h +++ b/src/core/hle/service/acc/acc_u1.h @@ -10,8 +10,8 @@ namespace Service::Account { class ACC_U1 final : public Module::Interface { public: - explicit ACC_U1(std::shared_ptr<Module> module, - std::shared_ptr<ProfileManager> profile_manager); + explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager, + Core::System& system); ~ACC_U1() override; }; diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h new file mode 100644 index 000000000..1f0577239 --- /dev/null +++ b/src/core/hle/service/acc/errors.h @@ -0,0 +1,14 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Account { + +constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; +constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41}; + +} // namespace Service::Account diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 49aa5908b..8f9986326 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -305,6 +305,17 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { return true; } +bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, + const ProfileData& data_new) { + const auto index = GetUserIndex(uuid); + if (index.has_value() && SetProfileBase(uuid, profile_new)) { + profiles[*index].data = data_new; + return true; + } + + return false; +} + void ProfileManager::ParseUserSaveFile() { FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index fd7abb541..5a6d28925 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -91,6 +91,8 @@ public: bool RemoveUser(Common::UUID uuid); bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new); + bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new, + const ProfileData& data_new); private: void ParseUserSaveFile(); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 3f201c821..797c9a06f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -29,7 +29,8 @@ #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/apm.h" +#include "core/hle/service/apm/controller.h" +#include "core/hle/service/apm/interface.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -55,7 +56,8 @@ struct LaunchParameters { }; static_assert(sizeof(LaunchParameters) == 0x88); -IWindowController::IWindowController() : ServiceFramework("IWindowController") { +IWindowController::IWindowController(Core::System& system_) + : ServiceFramework("IWindowController"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, @@ -74,7 +76,7 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") { IWindowController::~IWindowController() = default; void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) { - const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID(); + const u64 process_id = system.CurrentProcess()->GetProcessID(); LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id); @@ -230,11 +232,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} { IDebugFunctions::~IDebugFunctions() = default; -ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger) - : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) { +ISelfController::ISelfController(Core::System& system, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger) + : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "Exit"}, + {0, &ISelfController::Exit, "Exit"}, {1, &ISelfController::LockExit, "LockExit"}, {2, &ISelfController::UnlockExit, "UnlockExit"}, {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, @@ -265,13 +268,13 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger {65, nullptr, "ReportUserIsActive"}, {66, nullptr, "GetCurrentIlluminance"}, {67, nullptr, "IsIlluminanceAvailable"}, - {68, nullptr, "SetAutoSleepDisabled"}, - {69, nullptr, "IsAutoSleepDisabled"}, + {68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"}, + {69, &ISelfController::IsAutoSleepDisabled, "IsAutoSleepDisabled"}, {70, nullptr, "ReportMultimediaError"}, {71, nullptr, "GetCurrentIlluminanceEx"}, {80, nullptr, "SetWirelessPriorityMode"}, - {90, nullptr, "GetAccumulatedSuspendedTickValue"}, - {91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"}, + {90, &ISelfController::GetAccumulatedSuspendedTickValue, "GetAccumulatedSuspendedTickValue"}, + {91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"}, {100, nullptr, "SetAlbumImageTakenNotificationEnabled"}, {1000, nullptr, "GetDebugStorageChannel"}, }; @@ -279,22 +282,44 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "ISelfController:LaunchableEvent"); + + // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is + // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple + // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not + // suspended if the event has previously been created by a call to + // GetAccumulatedSuspendedTickChangedEvent. + accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent"); + accumulated_suspended_tick_changed_event.writable->Signal(); } ISelfController::~ISelfController() = default; +void ISelfController::Exit(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called"); + + system.Shutdown(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLock(true); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_AM, "(STUBBED) called"); + LOG_DEBUG(Service_AM, "called"); + + system.SetExitLock(false); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -444,8 +469,54 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c rb.Push<u32>(idle_time_detection_extension); } -AppletMessageQueue::AppletMessageQueue() { - auto& kernel = Core::System::GetInstance().Kernel(); +void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + is_auto_sleep_disabled = rp.Pop<bool>(); + + // On the system itself, if the previous state of is_auto_sleep_disabled + // differed from the current value passed in, it'd signify the internal + // window manager to update (and also increment some statistics like update counts) + // + // It'd also indicate this change to an idle handling context. + // + // However, given we're emulating this behavior, most of this can be ignored + // and it's sufficient to simply set the member variable for querying via + // IsAutoSleepDisabled(). + + LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ISelfController::IsAutoSleepDisabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(is_auto_sleep_disabled); +} + +void ISelfController::GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + // This command returns the total number of system ticks since ISelfController creation + // where the game was suspended. Since Yuzu doesn't implement game suspension, this command + // can just always return 0 ticks. + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(0); +} + +void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called."); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable); +} + +AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) { on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "AMMessageQueue:OnMessageRecieved"); on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair( @@ -492,8 +563,13 @@ void AppletMessageQueue::OperationModeChanged() { on_operation_mode_changed.writable->Signal(); } -ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue) - : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) { +void AppletMessageQueue::RequestExit() { + PushMessage(AppletMessage::ExitRequested); +} + +ICommonStateGetter::ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue) + : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { // clang-format off static const FunctionInfo functions[] = { {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"}, @@ -526,7 +602,7 @@ ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_q {63, nullptr, "GetHdcpAuthenticationStateChangeEvent"}, {64, nullptr, "SetTvPowerStateMatchingMode"}, {65, nullptr, "GetApplicationIdByContentActionName"}, - {66, nullptr, "SetCpuBoostMode"}, + {66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"}, {80, nullptr, "PerformSystemButtonPressingIfInFocus"}, {90, nullptr, "SetPerformanceConfigurationChangedNotification"}, {91, nullptr, "GetCurrentPerformanceConfiguration"}, @@ -607,6 +683,16 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& } } +void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_AM, "called, forwarding to APM:SYS"); + + const auto& sm = system.ServiceManager(); + const auto apm_sys = sm.GetService<APM::APM_Sys>("apm:sys"); + ASSERT(apm_sys != nullptr); + + apm_sys->SetCpuBoostMode(ctx); +} + IStorage::IStorage(std::vector<u8> buffer) : ServiceFramework("IStorage"), buffer(std::move(buffer)) { // clang-format off @@ -635,13 +721,11 @@ void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { } void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) { - const bool use_docked_mode{Settings::values.use_docked_mode}; - LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode); + LOG_DEBUG(Service_AM, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked - : APM::PerformanceMode::Handheld)); + rb.PushEnum(system.GetAPMController().GetCurrentPerformanceMode()); } class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> { @@ -871,7 +955,8 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { +ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_) + : ServiceFramework("ILibraryAppletCreator"), system{system_} { static const FunctionInfo functions[] = { {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, {1, nullptr, "TerminateAllLibraryApplets"}, @@ -893,7 +978,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", static_cast<u32>(applet_id), applet_mode); - const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; + const auto& applet_manager{system.GetAppletManager()}; const auto applet = applet_manager.GetApplet(applet_id); if (applet == nullptr) { @@ -931,8 +1016,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex const auto handle{rp.Pop<Kernel::Handle>()}; const auto transfer_mem = - Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>( - handle); + system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); if (transfer_mem == nullptr) { LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle); @@ -950,7 +1034,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); } -IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") { +IApplicationFunctions::IApplicationFunctions(Core::System& system_) + : ServiceFramework("IApplicationFunctions"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"}, @@ -989,6 +1074,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF {120, nullptr, "ExecuteProgram"}, {121, nullptr, "ClearUserChannel"}, {122, nullptr, "UnpopToUserChannel"}, + {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {500, nullptr, "StartContinuousRecordingFlushForDebug"}, {1000, nullptr, "CreateMovieMaker"}, {1001, nullptr, "PrepareForJit"}, @@ -996,6 +1082,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF // clang-format on RegisterHandlers(functions); + + auto& kernel = system.Kernel(); + gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); } IApplicationFunctions::~IApplicationFunctions() = default; @@ -1070,13 +1160,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - u128 uid = rp.PopRaw<u128>(); // What does this do? - LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); + u128 user_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); + + FileSys::SaveDataDescriptor descriptor{}; + descriptor.title_id = Core::CurrentProcess()->GetTitleID(); + descriptor.user_id = user_id; + descriptor.type = FileSys::SaveDataType::SaveData; + const auto res = system.GetFileSystemController().CreateSaveData( + FileSys::SaveDataSpaceId::NandUser, descriptor); IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(RESULT_SUCCESS); + rb.Push(res.Code()); rb.Push<u64>(0); -} // namespace Service::AM +} void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { // Takes an input u32 Result, no output. @@ -1107,7 +1205,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // Get supported languages from NACP, if possible // Default to 0 (all languages supported) u32 supported_languages = 0; - FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()}; + FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; const auto res = pm.GetControlMetadata(); if (res.first != nullptr) { @@ -1115,8 +1213,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { } // Call IApplicationManagerInterface implementation. - auto& service_manager = Core::System::GetInstance().ServiceManager(); - auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2"); + auto& service_manager = system.ServiceManager(); + auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2"); auto app_man = ns_am2->GetApplicationManagerInterface(); // Get desired application language @@ -1188,8 +1286,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) { "new_journal={:016X}", static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); - FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id, - {new_normal_size, new_journal_size}); + system.GetFileSystemController().WriteSaveDataSize( + type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -1208,8 +1306,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), user_id[1], user_id[0]); - const auto size = - FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id); + const auto size = system.GetFileSystemController().ReadSaveDataSize( + type, system.CurrentProcess()->GetTitleID(), user_id); IPC::ResponseBuilder rb{ctx, 6}; rb.Push(RESULT_SUCCESS); @@ -1217,14 +1315,22 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) { rb.Push(size.journal); } +void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(gpu_error_detected_event.readable); +} + void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { - auto message_queue = std::make_shared<AppletMessageQueue>(); - message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on - // game boot + std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) { + auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); + // Needed on game boot + message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); - std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); - std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); + std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager); + std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager); std::make_shared<IdleSys>()->InstallAsService(service_manager); std::make_shared<OMM>()->InstallAsService(service_manager); std::make_shared<SPSM>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 991b7d47c..a3baeb673 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -10,12 +10,15 @@ #include "core/hle/kernel/writable_event.h" #include "core/hle/service/service.h" -namespace Service { -namespace NVFlinger { +namespace Kernel { +class KernelCore; +} + +namespace Service::NVFlinger { class NVFlinger; } -namespace AM { +namespace Service::AM { enum SystemLanguage { Japanese = 0, @@ -42,12 +45,13 @@ class AppletMessageQueue { public: enum class AppletMessage : u32 { NoMessage = 0, + ExitRequested = 4, FocusStateChanged = 15, OperationModeChanged = 30, PerformanceModeChanged = 31, }; - AppletMessageQueue(); + explicit AppletMessageQueue(Kernel::KernelCore& kernel); ~AppletMessageQueue(); const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const; @@ -56,6 +60,7 @@ public: AppletMessage PopMessage(); std::size_t GetMessageCount() const; void OperationModeChanged(); + void RequestExit(); private: std::queue<AppletMessage> messages; @@ -65,12 +70,14 @@ private: class IWindowController final : public ServiceFramework<IWindowController> { public: - IWindowController(); + explicit IWindowController(Core::System& system_); ~IWindowController() override; private: void GetAppletResourceUserId(Kernel::HLERequestContext& ctx); void AcquireForegroundRights(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IAudioController final : public ServiceFramework<IAudioController> { @@ -113,10 +120,12 @@ public: class ISelfController final : public ServiceFramework<ISelfController> { public: - explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + explicit ISelfController(Core::System& system_, + std::shared_ptr<NVFlinger::NVFlinger> nvflinger_); ~ISelfController() override; private: + void Exit(Kernel::HLERequestContext& ctx); void LockExit(Kernel::HLERequestContext& ctx); void UnlockExit(Kernel::HLERequestContext& ctx); void EnterFatalSection(Kernel::HLERequestContext& ctx); @@ -133,16 +142,26 @@ private: void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx); void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx); + void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx); + void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); + void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); std::shared_ptr<NVFlinger::NVFlinger> nvflinger; Kernel::EventPair launchable_event; + Kernel::EventPair accumulated_suspended_tick_changed_event; + u32 idle_time_detection_extension = 0; u64 num_fatal_sections_entered = 0; + bool is_auto_sleep_disabled = false; + + Core::System& system; }; class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { public: - explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue); + explicit ICommonStateGetter(Core::System& system, + std::shared_ptr<AppletMessageQueue> msg_queue); ~ICommonStateGetter() override; private: @@ -164,7 +183,9 @@ private: void GetPerformanceMode(Kernel::HLERequestContext& ctx); void GetBootMode(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); + void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + Core::System& system; std::shared_ptr<AppletMessageQueue> msg_queue; }; @@ -198,18 +219,20 @@ private: class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { public: - ILibraryAppletCreator(); + explicit ILibraryAppletCreator(Core::System& system_); ~ILibraryAppletCreator() override; private: void CreateLibraryApplet(Kernel::HLERequestContext& ctx); void CreateStorage(Kernel::HLERequestContext& ctx); void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); + + Core::System& system; }; class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { public: - IApplicationFunctions(); + explicit IApplicationFunctions(Core::System& system_); ~IApplicationFunctions() override; private: @@ -230,6 +253,10 @@ private: void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx); void EndBlockingHomeButton(Kernel::HLERequestContext& ctx); void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); + void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); + + Kernel::EventPair gpu_error_detected_event; + Core::System& system; }; class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> { @@ -261,7 +288,6 @@ public: /// Registers all AM services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager, - std::shared_ptr<NVFlinger::NVFlinger> nvflinger); + std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system); -} // namespace AM -} // namespace Service +} // namespace Service::AM diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp index 488add8e7..e454b77d8 100644 --- a/src/core/hle/service/am/applet_ae.cpp +++ b/src/core/hle/service/am/applet_ae.cpp @@ -4,6 +4,7 @@ #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -13,9 +14,10 @@ namespace Service::AM { class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { public: explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue) + std::shared_ptr<AppletMessageQueue> msg_queue, + Core::System& system) : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)) { + msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -40,7 +42,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -48,7 +50,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -56,7 +58,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -96,7 +98,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -104,19 +106,20 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { public: explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue) + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)) { + msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -143,7 +146,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -151,7 +154,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetWindowController(Kernel::HLERequestContext& ctx) { @@ -159,7 +162,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetAudioController(Kernel::HLERequestContext& ctx) { @@ -191,7 +194,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { @@ -219,6 +222,7 @@ private: } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { @@ -226,7 +230,7 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue); + rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue, system); } void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { @@ -234,7 +238,7 @@ void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); } void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { @@ -242,13 +246,13 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); + rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system); } AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue) + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)) { + msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h index 902db2665..2e3e45915 100644 --- a/src/core/hle/service/am/applet_ae.h +++ b/src/core/hle/service/am/applet_ae.h @@ -9,16 +9,22 @@ #include "core/hle/service/service.h" namespace Service { +namespace FileSystem { +class FileSystemController; +} + namespace NVFlinger { class NVFlinger; } namespace AM { +class AppletMessageQueue; + class AppletAE final : public ServiceFramework<AppletAE> { public: explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue); + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); ~AppletAE() override; const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; @@ -30,6 +36,7 @@ private: std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; } // namespace AM diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp index d3a0a1568..a2ffaa440 100644 --- a/src/core/hle/service/am/applet_oe.cpp +++ b/src/core/hle/service/am/applet_oe.cpp @@ -13,9 +13,9 @@ namespace Service::AM { class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { public: explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue) + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)) { + msg_queue(std::move(msg_queue)), system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, @@ -63,7 +63,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IWindowController>(); + rb.PushIpcInterface<IWindowController>(system); } void GetSelfController(Kernel::HLERequestContext& ctx) { @@ -71,7 +71,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISelfController>(nvflinger); + rb.PushIpcInterface<ISelfController>(system, nvflinger); } void GetCommonStateGetter(Kernel::HLERequestContext& ctx) { @@ -79,7 +79,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ICommonStateGetter>(msg_queue); + rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue); } void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) { @@ -87,7 +87,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ILibraryAppletCreator>(); + rb.PushIpcInterface<ILibraryAppletCreator>(system); } void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { @@ -95,11 +95,12 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationFunctions>(); + rb.PushIpcInterface<IApplicationFunctions>(system); } std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { @@ -107,13 +108,13 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue); + rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system); } AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue) + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system) : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), - msg_queue(std::move(msg_queue)) { + msg_queue(std::move(msg_queue)), system(system) { static const FunctionInfo functions[] = { {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, }; diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h index bbd0108ef..758da792d 100644 --- a/src/core/hle/service/am/applet_oe.h +++ b/src/core/hle/service/am/applet_oe.h @@ -9,16 +9,22 @@ #include "core/hle/service/service.h" namespace Service { +namespace FileSystem { +class FileSystemController; +} + namespace NVFlinger { class NVFlinger; } namespace AM { +class AppletMessageQueue; + class AppletOE final : public ServiceFramework<AppletOE> { public: explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, - std::shared_ptr<AppletMessageQueue> msg_queue); + std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system); ~AppletOE() override; const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; @@ -28,6 +34,7 @@ private: std::shared_ptr<NVFlinger::NVFlinger> nvflinger; std::shared_ptr<AppletMessageQueue> msg_queue; + Core::System& system; }; } // namespace AM diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 14fa92318..d2e35362f 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -23,8 +23,7 @@ namespace Service::AM::Applets { -AppletDataBroker::AppletDataBroker() { - auto& kernel = Core::System::GetInstance().Kernel(); +AppletDataBroker::AppletDataBroker(Kernel::KernelCore& kernel) { state_changed_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Manual, "ILibraryAppletAccessor:StateChangedEvent"); pop_out_data_event = Kernel::WritableEvent::CreateEventPair( @@ -35,12 +34,28 @@ AppletDataBroker::AppletDataBroker() { AppletDataBroker::~AppletDataBroker() = default; +AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const { + std::vector<std::vector<u8>> out_normal; + + for (const auto& storage : in_channel) { + out_normal.push_back(storage->GetData()); + } + + std::vector<std::vector<u8>> out_interactive; + + for (const auto& storage : in_interactive_channel) { + out_interactive.push_back(storage->GetData()); + } + + return {std::move(out_normal), std::move(out_interactive)}; +} + std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { if (out_channel.empty()) return nullptr; auto out = std::move(out_channel.front()); - out_channel.pop(); + out_channel.pop_front(); return out; } @@ -49,7 +64,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { return nullptr; auto out = std::move(in_channel.front()); - in_channel.pop(); + in_channel.pop_front(); return out; } @@ -58,7 +73,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { return nullptr; auto out = std::move(out_interactive_channel.front()); - out_interactive_channel.pop(); + out_interactive_channel.pop_front(); return out; } @@ -67,25 +82,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { return nullptr; auto out = std::move(in_interactive_channel.front()); - in_interactive_channel.pop(); + in_interactive_channel.pop_front(); return out; } void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { - in_channel.push(std::make_unique<IStorage>(storage)); + in_channel.push_back(std::make_unique<IStorage>(storage)); } void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { - out_channel.push(std::make_unique<IStorage>(storage)); + out_channel.push_back(std::make_unique<IStorage>(storage)); pop_out_data_event.writable->Signal(); } void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { - in_interactive_channel.push(std::make_unique<IStorage>(storage)); + in_interactive_channel.push_back(std::make_unique<IStorage>(storage)); } void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { - out_interactive_channel.push(std::make_unique<IStorage>(storage)); + out_interactive_channel.push_back(std::make_unique<IStorage>(storage)); pop_interactive_out_data_event.writable->Signal(); } @@ -105,7 +120,7 @@ Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent( return state_changed_event.readable; } -Applet::Applet() = default; +Applet::Applet(Kernel::KernelCore& kernel_) : broker{kernel_} {} Applet::~Applet() = default; @@ -123,12 +138,14 @@ void Applet::Initialize() { AppletFrontendSet::AppletFrontendSet() = default; -AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, - ProfileSelect profile_select, - SoftwareKeyboard software_keyboard, WebBrowser web_browser) - : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move( - profile_select)}, - software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {} +AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, + PhotoViewer photo_viewer, ProfileSelect profile_select, + SoftwareKeyboard software_keyboard, WebBrowser web_browser, + ECommerceApplet e_commerce) + : parental_controls{std::move(parental_controls)}, error{std::move(error)}, + photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, + software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, + e_commerce{std::move(e_commerce)} {} AppletFrontendSet::~AppletFrontendSet() = default; @@ -136,11 +153,13 @@ AppletFrontendSet::AppletFrontendSet(AppletFrontendSet&&) noexcept = default; AppletFrontendSet& AppletFrontendSet::operator=(AppletFrontendSet&&) noexcept = default; -AppletManager::AppletManager() = default; +AppletManager::AppletManager(Core::System& system_) : system{system_} {} AppletManager::~AppletManager() = default; void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { + if (set.parental_controls != nullptr) + frontend.parental_controls = std::move(set.parental_controls); if (set.error != nullptr) frontend.error = std::move(set.error); if (set.photo_viewer != nullptr) @@ -151,17 +170,21 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { frontend.software_keyboard = std::move(set.software_keyboard); if (set.web_browser != nullptr) frontend.web_browser = std::move(set.web_browser); + if (set.e_commerce != nullptr) + frontend.e_commerce = std::move(set.e_commerce); } void AppletManager::SetDefaultAppletFrontendSet() { - frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); - frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); - frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>(); - frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>(); - frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); + ClearAll(); + SetDefaultAppletsIfMissing(); } void AppletManager::SetDefaultAppletsIfMissing() { + if (frontend.parental_controls == nullptr) { + frontend.parental_controls = + std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); + } + if (frontend.error == nullptr) { frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); } @@ -182,6 +205,10 @@ void AppletManager::SetDefaultAppletsIfMissing() { if (frontend.web_browser == nullptr) { frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); } + + if (frontend.e_commerce == nullptr) { + frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>(); + } } void AppletManager::ClearAll() { @@ -190,21 +217,26 @@ void AppletManager::ClearAll() { std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { switch (id) { + case AppletId::Auth: + return std::make_shared<Auth>(system, *frontend.parental_controls); case AppletId::Error: - return std::make_shared<Error>(*frontend.error); + return std::make_shared<Error>(system, *frontend.error); case AppletId::ProfileSelect: - return std::make_shared<ProfileSelect>(*frontend.profile_select); + return std::make_shared<ProfileSelect>(system, *frontend.profile_select); case AppletId::SoftwareKeyboard: - return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); + return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard); case AppletId::PhotoViewer: - return std::make_shared<PhotoViewer>(*frontend.photo_viewer); + return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer); + case AppletId::LibAppletShop: + return std::make_shared<WebBrowser>(system, *frontend.web_browser, + frontend.e_commerce.get()); case AppletId::LibAppletOff: - return std::make_shared<WebBrowser>(*frontend.web_browser); + return std::make_shared<WebBrowser>(system, *frontend.web_browser); default: UNIMPLEMENTED_MSG( "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", static_cast<u8>(id)); - return std::make_shared<StubApplet>(); + return std::make_shared<StubApplet>(system, id); } } diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index b46e10a4a..764c3418c 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -12,14 +12,24 @@ union ResultCode; +namespace Core { +class System; +} + namespace Core::Frontend { +class ECommerceApplet; class ErrorApplet; +class ParentalControlsApplet; class PhotoViewerApplet; class ProfileSelectApplet; class SoftwareKeyboardApplet; class WebBrowserApplet; } // namespace Core::Frontend +namespace Kernel { +class KernelCore; +} + namespace Service::AM { class IStorage; @@ -51,9 +61,17 @@ enum class AppletId : u32 { class AppletDataBroker final { public: - AppletDataBroker(); + explicit AppletDataBroker(Kernel::KernelCore& kernel_); ~AppletDataBroker(); + struct RawChannelData { + std::vector<std::vector<u8>> normal; + std::vector<std::vector<u8>> interactive; + }; + + // Retrieves but does not pop the data sent to applet. + RawChannelData PeekDataToAppletForDebug() const; + std::unique_ptr<IStorage> PopNormalDataToGame(); std::unique_ptr<IStorage> PopNormalDataToApplet(); @@ -76,16 +94,16 @@ private: // Queues are named from applet's perspective // PopNormalDataToApplet and PushNormalDataFromGame - std::queue<std::unique_ptr<IStorage>> in_channel; + std::deque<std::unique_ptr<IStorage>> in_channel; // PopNormalDataToGame and PushNormalDataFromApplet - std::queue<std::unique_ptr<IStorage>> out_channel; + std::deque<std::unique_ptr<IStorage>> out_channel; // PopInteractiveDataToApplet and PushInteractiveDataFromGame - std::queue<std::unique_ptr<IStorage>> in_interactive_channel; + std::deque<std::unique_ptr<IStorage>> in_interactive_channel; // PopInteractiveDataToGame and PushInteractiveDataFromApplet - std::queue<std::unique_ptr<IStorage>> out_interactive_channel; + std::deque<std::unique_ptr<IStorage>> out_interactive_channel; Kernel::EventPair state_changed_event; @@ -98,7 +116,7 @@ private: class Applet { public: - Applet(); + explicit Applet(Kernel::KernelCore& kernel_); virtual ~Applet(); virtual void Initialize(); @@ -137,15 +155,19 @@ protected: }; struct AppletFrontendSet { + using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; + using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>; AppletFrontendSet(); - AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select, - SoftwareKeyboard software_keyboard, WebBrowser web_browser); + AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, + PhotoViewer photo_viewer, ProfileSelect profile_select, + SoftwareKeyboard software_keyboard, WebBrowser web_browser, + ECommerceApplet e_commerce); ~AppletFrontendSet(); AppletFrontendSet(const AppletFrontendSet&) = delete; @@ -154,16 +176,18 @@ struct AppletFrontendSet { AppletFrontendSet(AppletFrontendSet&&) noexcept; AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; + ParentalControlsApplet parental_controls; ErrorApplet error; PhotoViewer photo_viewer; ProfileSelect profile_select; SoftwareKeyboard software_keyboard; WebBrowser web_browser; + ECommerceApplet e_commerce; }; class AppletManager { public: - AppletManager(); + explicit AppletManager(Core::System& system_); ~AppletManager(); void SetAppletFrontendSet(AppletFrontendSet set); @@ -175,6 +199,7 @@ public: private: AppletFrontendSet frontend; + Core::System& system; }; } // namespace Applets diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index 04774bedc..a7db26725 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -9,8 +9,10 @@ #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/error.h" +#include "core/hle/kernel/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 { @@ -83,7 +85,8 @@ ResultCode Decode64BitError(u64 error) { } // Anonymous namespace -Error::Error(const Core::Frontend::ErrorApplet& frontend) : frontend(frontend) {} +Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} Error::~Error() = default; @@ -143,9 +146,12 @@ void Error::Execute() { } 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: @@ -156,14 +162,18 @@ void Error::Execute() { const auto& detail_text = system ? args->system_error.detail_text : args->application_error.detail_text; - frontend.ShowCustomErrorText( - error_code, - Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), - Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), - callback); + 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; diff --git a/src/core/hle/service/am/applets/error.h b/src/core/hle/service/am/applets/error.h index a3590d181..a105cdb0c 100644 --- a/src/core/hle/service/am/applets/error.h +++ b/src/core/hle/service/am/applets/error.h @@ -7,6 +7,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class ErrorAppletMode : u8 { @@ -21,7 +25,7 @@ enum class ErrorAppletMode : u8 { class Error final : public Applet { public: - explicit Error(const Core::Frontend::ErrorApplet& frontend); + explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_); ~Error() override; void Initialize() override; @@ -42,6 +46,7 @@ private: std::unique_ptr<ErrorArguments> args; bool complete = false; + Core::System& system; }; } // 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 index c591b9ac2..328438a1d 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <string> +#include <string_view> #include "common/assert.h" #include "common/hex_util.h" @@ -13,28 +13,147 @@ #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 { -static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) { +constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; + +static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { const auto data = storage->GetData(); LOG_INFO(Service_AM, - "called (STUBBED), during {} recieved normal data with size={:08X}, data={}", - prefix, data.size(), Common::HexVectorToString(data)); + "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 {} recieved interactive data with size={:08X}, data={}", - prefix, data.size(), Common::HexVectorToString(data)); + "called (STUBBED), during {} received interactive data with size={:08X}, data={}", + prefix, data.size(), Common::HexToString(data)); } } -PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} +Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} + +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 ? RESULT_SUCCESS : 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}", + static_cast<u32>(type), arg0, arg1, arg2); + }; + + switch (type) { + case AuthAppletType::ShowParentalAuthentication: { + const auto callback = [this](bool successful) { AuthFinished(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 successful) { + this->successful = 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(IStorage{out}); + broker.SignalStateChanged(); +} + +PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {} PhotoViewer::~PhotoViewer() = default; @@ -68,7 +187,7 @@ void PhotoViewer::Execute() { const auto callback = [this] { ViewFinished(); }; switch (mode) { case PhotoViewerAppletMode::CurrentApp: - frontend.ShowPhotosForApplication(Core::CurrentProcess()->GetTitleID(), callback); + frontend.ShowPhotosForApplication(system.CurrentProcess()->GetTitleID(), callback); break; case PhotoViewerAppletMode::AllApps: frontend.ShowAllPhotos(callback); @@ -83,13 +202,21 @@ void PhotoViewer::ViewFinished() { broker.SignalStateChanged(); } -StubApplet::StubApplet() = default; +StubApplet::StubApplet(Core::System& system_, AppletId id_) + : Applet{system_.Kernel()}, 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"); } diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h index 2dd255d7c..cfa2df369 100644 --- a/src/core/hle/service/am/applets/general_backend.h +++ b/src/core/hle/service/am/applets/general_backend.h @@ -6,8 +6,42 @@ #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { +enum class AuthAppletType : u32 { + ShowParentalAuthentication, + RegisterParentalPasscode, + ChangeParentalPasscode, +}; + +class Auth final : public Applet { +public: + explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_); + ~Auth() override; + + void Initialize() override; + bool TransactionComplete() const override; + ResultCode GetStatus() const override; + void ExecuteInteractive() override; + void Execute() override; + + void AuthFinished(bool successful = true); + +private: + Core::Frontend::ParentalControlsApplet& frontend; + bool complete = false; + bool successful = false; + + AuthAppletType type = AuthAppletType::ShowParentalAuthentication; + u8 arg0 = 0; + u8 arg1 = 0; + u8 arg2 = 0; +}; + enum class PhotoViewerAppletMode : u8 { CurrentApp = 0, AllApps = 1, @@ -15,7 +49,7 @@ enum class PhotoViewerAppletMode : u8 { class PhotoViewer final : public Applet { public: - explicit PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend); + explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_); ~PhotoViewer() override; void Initialize() override; @@ -30,11 +64,12 @@ private: const Core::Frontend::PhotoViewerApplet& frontend; bool complete = false; PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp; + Core::System& system; }; class StubApplet final : public Applet { public: - StubApplet(); + explicit StubApplet(Core::System& system_, AppletId id_); ~StubApplet() override; void Initialize() override; @@ -43,6 +78,10 @@ public: ResultCode GetStatus() const override; void ExecuteInteractive() override; void Execute() override; + +private: + AppletId id; + Core::System& system; }; } // 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 index 57b5419e8..3eba696ca 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -15,8 +15,9 @@ namespace Service::AM::Applets { constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1}; -ProfileSelect::ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend) - : frontend(frontend) {} +ProfileSelect::ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} ProfileSelect::~ProfileSelect() = default; diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h index 563cd744a..16364ead7 100644 --- a/src/core/hle/service/am/applets/profile_select.h +++ b/src/core/hle/service/am/applets/profile_select.h @@ -11,6 +11,10 @@ #include "core/hle/result.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { struct UserSelectionConfig { @@ -29,7 +33,8 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco class ProfileSelect final : public Applet { public: - explicit ProfileSelect(const Core::Frontend::ProfileSelectApplet& frontend); + explicit ProfileSelect(Core::System& system_, + const Core::Frontend::ProfileSelectApplet& frontend_); ~ProfileSelect() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index e197990f7..748559cd0 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -39,8 +39,9 @@ static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( return params; } -SoftwareKeyboard::SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend) - : frontend(frontend) {} +SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_) + : Applet{system_.Kernel()}, frontend(frontend_) {} SoftwareKeyboard::~SoftwareKeyboard() = default; diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index 0fbc43e51..ef4801fc6 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h @@ -16,6 +16,10 @@ union ResultCode; +namespace Core { +class System; +} + namespace Service::AM::Applets { enum class KeysetDisable : u32 { @@ -55,7 +59,8 @@ static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect siz class SoftwareKeyboard final : public Applet { public: - explicit SoftwareKeyboard(const Core::Frontend::SoftwareKeyboardApplet& frontend); + explicit SoftwareKeyboard(Core::System& system_, + const Core::Frontend::SoftwareKeyboardApplet& frontend_); ~SoftwareKeyboard() override; void Initialize() override; diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 7878f5136..32283e819 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -19,7 +19,9 @@ #include "core/file_sys/nca_metadata.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_types.h" +#include "core/frontend/applets/general_frontend.h" #include "core/frontend/applets/web_browser.h" #include "core/hle/kernel/process.h" #include "core/hle/service/am/applets/web_browser.h" @@ -28,74 +30,188 @@ namespace Service::AM::Applets { -// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not -// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant, -// but some may be worth an implementation. -constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6; +enum class WebArgTLVType : u16 { + InitialURL = 0x1, + ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name. + CallbackURL = 0x3, + CallbackableURL = 0x4, + ApplicationID = 0x5, + DocumentPath = 0x6, + DocumentKind = 0x7, + SystemDataID = 0x8, + ShareStartPage = 0x9, + Whitelist = 0xA, + News = 0xB, + UserID = 0xE, + AlbumEntry0 = 0xF, + ScreenShotEnabled = 0x10, + EcClientCertEnabled = 0x11, + Unk12 = 0x12, + PlayReportEnabled = 0x13, + Unk14 = 0x14, + Unk15 = 0x15, + BootDisplayKind = 0x17, + BackgroundKind = 0x18, + FooterEnabled = 0x19, + PointerEnabled = 0x1A, + LeftStickMode = 0x1B, + KeyRepeatFrame1 = 0x1C, + KeyRepeatFrame2 = 0x1D, + BootAsMediaPlayerInv = 0x1E, + DisplayUrlKind = 0x1F, + BootAsMediaPlayer = 0x21, + ShopJumpEnabled = 0x22, + MediaAutoPlayEnabled = 0x23, + LobbyParameter = 0x24, + ApplicationAlbumEntry = 0x26, + JsExtensionEnabled = 0x27, + AdditionalCommentText = 0x28, + TouchEnabledOnContents = 0x29, + UserAgentAdditionalString = 0x2A, + AdditionalMediaData0 = 0x2B, + MediaPlayerAutoCloseEnabled = 0x2C, + PageCacheEnabled = 0x2D, + WebAudioEnabled = 0x2E, + Unk2F = 0x2F, + YouTubeVideoWhitelist = 0x31, + FooterFixedKind = 0x32, + PageFadeEnabled = 0x33, + MediaCreatorApplicationRatingAge = 0x34, + BootLoadingIconEnabled = 0x35, + PageScrollIndicationEnabled = 0x36, + MediaPlayerSpeedControlEnabled = 0x37, + AlbumEntry1 = 0x38, + AlbumEntry2 = 0x39, + AlbumEntry3 = 0x3A, + AdditionalMediaData1 = 0x3B, + AdditionalMediaData2 = 0x3C, + AdditionalMediaData3 = 0x3D, + BootFooterButton = 0x3E, + OverrideWebAudioVolume = 0x3F, + OverrideMediaAudioVolume = 0x40, + BootMode = 0x41, + WebSessionEnabled = 0x42, +}; + +enum class ShimKind : u32 { + Shop = 1, + Login = 2, + Offline = 3, + Share = 4, + Web = 5, + Wifi = 6, + Lobby = 7, +}; + +enum class ShopWebTarget { + ApplicationInfo, + AddOnContentList, + SubscriptionList, + ConsumableItemList, + Home, + Settings, +}; + +namespace { -struct WebBufferHeader { +constexpr std::size_t SHIM_KIND_COUNT = 0x8; + +struct WebArgHeader { u16 count; - INSERT_PADDING_BYTES(6); + INSERT_PADDING_BYTES(2); + ShimKind kind; }; -static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size."); +static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); -struct WebArgumentHeader { - u16 type; +struct WebArgTLV { + WebArgTLVType type; u16 size; u32 offset; }; -static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size."); +static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size."); -struct WebArgumentResult { +struct WebCommonReturnValue { u32 result_code; + INSERT_PADDING_BYTES(0x4); std::array<char, 0x1000> last_url; u64 last_url_size; }; -static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size."); - -static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) { - WebBufferHeader header; - ASSERT(sizeof(WebBufferHeader) <= data.size()); - std::memcpy(&header, data.data(), sizeof(WebBufferHeader)); - - u64 offset = sizeof(WebBufferHeader); - for (u16 i = 0; i < header.count; ++i) { - WebArgumentHeader arg; - ASSERT(offset + sizeof(WebArgumentHeader) <= data.size()); - std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader)); - offset += sizeof(WebArgumentHeader); - - if (arg.type == type) { - std::vector<u8> out(arg.size); - offset += arg.offset; - ASSERT(offset + arg.size <= data.size()); - std::memcpy(out.data(), data.data() + offset, out.size()); +static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); + +struct WebWifiPageArg { + INSERT_PADDING_BYTES(4); + std::array<char, 0x100> connection_test_url; + std::array<char, 0x400> initial_url; + std::array<u8, 0x10> nifm_network_uuid; + u32 nifm_requirement; +}; +static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size."); + +struct WebWifiReturnValue { + INSERT_PADDING_BYTES(4); + u32 result; +}; +static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size."); + +enum class OfflineWebSource : u32 { + OfflineHtmlPage = 0x1, + ApplicationLegalInformation = 0x2, + SystemDataPage = 0x3, +}; + +std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) { + if (arg.size() < sizeof(WebArgHeader)) + return {}; + + WebArgHeader header{}; + std::memcpy(&header, arg.data(), sizeof(WebArgHeader)); + + std::map<WebArgTLVType, std::vector<u8>> out; + u64 offset = sizeof(WebArgHeader); + for (std::size_t i = 0; i < header.count; ++i) { + if (arg.size() < (offset + sizeof(WebArgTLV))) + return out; + + WebArgTLV tlv{}; + std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV)); + offset += sizeof(WebArgTLV); + + offset += tlv.offset; + if (arg.size() < (offset + tlv.size)) return out; - } - offset += arg.offset + arg.size; + std::vector<u8> data(tlv.size); + std::memcpy(data.data(), arg.data() + offset, tlv.size); + offset += tlv.size; + + out.insert_or_assign(tlv.type, data); } - return {}; + return out; } -static FileSys::VirtualFile GetManualRomFS() { - auto& loader{Core::System::GetInstance().GetAppLoader()}; +FileSys::VirtualFile GetApplicationRomFS(const Core::System& system, u64 title_id, + FileSys::ContentRecordType type) { + const auto& installed{system.GetContentProvider()}; + const auto res = installed.GetEntry(title_id, type); - FileSys::VirtualFile out; - if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) - return out; + if (res != nullptr) { + return res->GetRomFS(); + } - const auto& installed{Core::System::GetInstance().GetContentProvider()}; - const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), - FileSys::ContentRecordType::Manual); + if (type == FileSys::ContentRecordType::Data) { + return FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + } - if (res != nullptr) - return res->GetRomFS(); return nullptr; } -WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} +} // Anonymous namespace + +WebBrowser::WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_) + : Applet{system_.Kernel()}, frontend(frontend_), + frontend_e_commerce(frontend_e_commerce_), system{system_} {} WebBrowser::~WebBrowser() = default; @@ -111,24 +227,12 @@ void WebBrowser::Initialize() { ASSERT(web_arg_storage != nullptr); const auto& web_arg = web_arg_storage->GetData(); - const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE); - filename = Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast<const char*>(url_data.data()), url_data.size()); + ASSERT(web_arg.size() >= 0x8); + std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind)); - temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + - "web_applet_manual", - FileUtil::DirectorySeparator::PlatformDefault); - FileUtil::DeleteDirRecursively(temporary_dir); - - manual_romfs = GetManualRomFS(); - if (manual_romfs == nullptr) { - status = ResultCode(-1); - LOG_ERROR(Service_AM, "Failed to find manual for current process!"); - } + args = GetWebArguments(web_arg); - filename = - FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename, - FileUtil::DirectorySeparator::PlatformDefault); + InitializeInternal(); } bool WebBrowser::TransactionComplete() const { @@ -144,25 +248,26 @@ void WebBrowser::ExecuteInteractive() { } void WebBrowser::Execute() { - if (complete) + if (complete) { return; + } if (status != RESULT_SUCCESS) { complete = true; return; } - frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); + ExecuteInternal(); } void WebBrowser::UnpackRomFS() { if (unpacked) return; - ASSERT(manual_romfs != nullptr); + ASSERT(offline_romfs != nullptr); const auto dir = - FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard); - const auto& vfs{Core::System::GetInstance().GetFilesystem()}; + FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + const auto& vfs{system.GetFilesystem()}; const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite); FileSys::VfsRawCopyD(dir, temp_dir); @@ -172,17 +277,275 @@ void WebBrowser::UnpackRomFS() { void WebBrowser::Finalize() { complete = true; - WebArgumentResult out{}; + WebCommonReturnValue out{}; out.result_code = 0; out.last_url_size = 0; - std::vector<u8> data(sizeof(WebArgumentResult)); - std::memcpy(data.data(), &out, sizeof(WebArgumentResult)); + std::vector<u8> data(sizeof(WebCommonReturnValue)); + std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); broker.PushNormalDataFromApplet(IStorage{data}); broker.SignalStateChanged(); + if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { + FileUtil::DeleteDirRecursively(temporary_dir); + } +} + +void WebBrowser::InitializeInternal() { + using WebAppletInitializer = void (WebBrowser::*)(); + + constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{ + nullptr, &WebBrowser::InitializeShop, + nullptr, &WebBrowser::InitializeOffline, + nullptr, nullptr, + nullptr, nullptr, + }; + + const auto index = static_cast<u32>(kind); + + if (index > functions.size() || functions[index] == nullptr) { + LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index); + return; + } + + const auto function = functions[index]; + (this->*function)(); +} + +void WebBrowser::ExecuteInternal() { + using WebAppletExecutor = void (WebBrowser::*)(); + + constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{ + nullptr, &WebBrowser::ExecuteShop, + nullptr, &WebBrowser::ExecuteOffline, + nullptr, nullptr, + nullptr, nullptr, + }; + + const auto index = static_cast<u32>(kind); + + if (index > functions.size() || functions[index] == nullptr) { + LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index); + return; + } + + const auto function = functions[index]; + (this->*function)(); +} + +void WebBrowser::InitializeShop() { + if (frontend_e_commerce == nullptr) { + LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!"); + status = ResultCode(-1); + return; + } + + const auto user_id_data = args.find(WebArgTLVType::UserID); + + user_id = std::nullopt; + if (user_id_data != args.end()) { + user_id = u128{}; + std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128)); + } + + const auto url = args.find(WebArgTLVType::ShopArgumentsURL); + + if (url == args.end()) { + LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!"); + status = ResultCode(-1); + return; + } + + std::vector<std::string> split_query; + Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(url->second.data()), url->second.size()), + '?', split_query); + + // 2 -> Main URL '?' Query Parameters + // Less is missing info, More is malformed + if (split_query.size() != 2) { + LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed"); + status = ResultCode(-1); + return; + } + + std::vector<std::string> queries; + Common::SplitString(split_query[1], '&', queries); + + const auto split_single_query = + [](const std::string& in) -> std::pair<std::string, std::string> { + const auto index = in.find('='); + if (index == std::string::npos || index == in.size() - 1) { + return {in, ""}; + } + + return {in.substr(0, index), in.substr(index + 1)}; + }; + + std::transform(queries.begin(), queries.end(), + std::inserter(shop_query, std::next(shop_query.begin())), split_single_query); + + const auto scene = shop_query.find("scene"); + + if (scene == shop_query.end()) { + LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!"); + status = ResultCode(-1); + return; + } + + const std::map<std::string, ShopWebTarget, std::less<>> target_map{ + {"product_detail", ShopWebTarget::ApplicationInfo}, + {"aocs", ShopWebTarget::AddOnContentList}, + {"subscriptions", ShopWebTarget::SubscriptionList}, + {"consumption", ShopWebTarget::ConsumableItemList}, + {"settings", ShopWebTarget::Settings}, + {"top", ShopWebTarget::Home}, + }; + + const auto target = target_map.find(scene->second); + if (target == target_map.end()) { + LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second); + status = ResultCode(-1); + return; + } + + shop_web_target = target->second; + + const auto title_id_data = shop_query.find("dst_app_id"); + if (title_id_data != shop_query.end()) { + title_id = std::stoull(title_id_data->second, nullptr, 0x10); + } + + const auto mode_data = shop_query.find("mode"); + if (mode_data != shop_query.end()) { + shop_full_display = mode_data->second == "full"; + } +} + +void WebBrowser::InitializeOffline() { + if (args.find(WebArgTLVType::DocumentPath) == args.end() || + args.find(WebArgTLVType::DocumentKind) == args.end() || + args.find(WebArgTLVType::ApplicationID) == args.end()) { + status = ResultCode(-1); + LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!"); + } + + const auto url_data = args[WebArgTLVType::DocumentPath]; + filename = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(url_data.data()), url_data.size()); + + OfflineWebSource source; + ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4); + std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource)); + + constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{ + "manual", + "legal", + "system", + }; + + temporary_dir = + FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], + FileUtil::DirectorySeparator::PlatformDefault); FileUtil::DeleteDirRecursively(temporary_dir); + + u64 title_id = 0; // 0 corresponds to current process + ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); + std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64)); + FileSys::ContentRecordType type = FileSys::ContentRecordType::Data; + + switch (source) { + case OfflineWebSource::OfflineHtmlPage: + // While there is an AppID TLV field, in official SW this is always ignored. + title_id = 0; + type = FileSys::ContentRecordType::HtmlDocument; + break; + case OfflineWebSource::ApplicationLegalInformation: + type = FileSys::ContentRecordType::LegalInformation; + break; + case OfflineWebSource::SystemDataPage: + type = FileSys::ContentRecordType::Data; + break; + } + + if (title_id == 0) { + title_id = system.CurrentProcess()->GetTitleID(); + } + + offline_romfs = GetApplicationRomFS(system, title_id, type); + if (offline_romfs == nullptr) { + status = ResultCode(-1); + LOG_ERROR(Service_AM, "Failed to find offline data for request!"); + } + + std::string path_additional_directory; + if (source == OfflineWebSource::OfflineHtmlPage) { + path_additional_directory = std::string(DIR_SEP).append("html-document"); + } + + filename = + FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, + FileUtil::DirectorySeparator::PlatformDefault); +} + +void WebBrowser::ExecuteShop() { + const auto callback = [this]() { Finalize(); }; + + const auto check_optional_parameter = [this](const auto& p) { + if (!p.has_value()) { + LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!"); + status = ResultCode(-1); + return false; + } + + return true; + }; + + switch (shop_web_target) { + case ShopWebTarget::ApplicationInfo: + if (!check_optional_parameter(title_id)) + return; + frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id, + shop_full_display, shop_extra_parameter); + break; + case ShopWebTarget::AddOnContentList: + if (!check_optional_parameter(title_id)) + return; + frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display); + break; + case ShopWebTarget::ConsumableItemList: + if (!check_optional_parameter(title_id)) + return; + frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id); + break; + case ShopWebTarget::Home: + if (!check_optional_parameter(user_id)) + return; + if (!check_optional_parameter(shop_full_display)) + return; + frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display); + break; + case ShopWebTarget::Settings: + if (!check_optional_parameter(user_id)) + return; + if (!check_optional_parameter(shop_full_display)) + return; + frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display); + break; + case ShopWebTarget::SubscriptionList: + if (!check_optional_parameter(title_id)) + return; + frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id); + break; + default: + UNREACHABLE(); + } +} + +void WebBrowser::ExecuteOffline() { + frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); } } // 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 index 7e0f34c7d..8d4027411 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h @@ -4,15 +4,26 @@ #pragma once +#include <map> #include "core/file_sys/vfs_types.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/applets/applets.h" +namespace Core { +class System; +} + namespace Service::AM::Applets { +enum class ShimKind : u32; +enum class ShopWebTarget; +enum class WebArgTLVType : u16; + class WebBrowser final : public Applet { public: - WebBrowser(Core::Frontend::WebBrowserApplet& frontend); + WebBrowser(Core::System& system_, Core::Frontend::WebBrowserApplet& frontend_, + Core::Frontend::ECommerceApplet* frontend_e_commerce_ = nullptr); + ~WebBrowser() override; void Initialize() override; @@ -32,15 +43,41 @@ public: void Finalize(); private: + void InitializeInternal(); + void ExecuteInternal(); + + // Specific initializers for the types of web applets + void InitializeShop(); + void InitializeOffline(); + + // Specific executors for the types of web applets + void ExecuteShop(); + void ExecuteOffline(); + Core::Frontend::WebBrowserApplet& frontend; + // Extra frontends for specialized functions + Core::Frontend::ECommerceApplet* frontend_e_commerce; + bool complete = false; bool unpacked = false; ResultCode status = RESULT_SUCCESS; - FileSys::VirtualFile manual_romfs; + ShimKind kind; + std::map<WebArgTLVType, std::vector<u8>> args; + + FileSys::VirtualFile offline_romfs; std::string temporary_dir; std::string filename; + + ShopWebTarget shop_web_target; + std::map<std::string, std::string, std::less<>> shop_query; + std::optional<u64> title_id = 0; + std::optional<u128> user_id; + std::optional<bool> shop_full_display; + std::string shop_extra_parameter; + + Core::System& system; }; } // namespace Service::AM::Applets diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index d3e97776b..e9cf1e840 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -29,9 +29,9 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { return (title_id & DLC_BASE_TITLE_ID_MASK) == base; } -static std::vector<u64> AccumulateAOCTitleIDs() { +static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) { std::vector<u64> add_on_content; - const auto& rcu = Core::System::GetInstance().GetContentProvider(); + const auto& rcu = system.GetContentProvider(); const auto list = rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), @@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() { return add_on_content; } -AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { +AOC_U::AOC_U(Core::System& system) + : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "CountAddOnContentByApplicationId"}, @@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, "GetAddOnContentListChanged:Event"); } @@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + const auto current = system.CurrentProcess()->GetTitleID(); const auto& disabled = Settings::values.disabled_addons[current]; if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { @@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, process_id); - const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + const auto current = system.CurrentProcess()->GetTitleID(); std::vector<u32> out; const auto& disabled = Settings::values.disabled_addons[current]; @@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + const auto title_id = system.CurrentProcess()->GetTitleID(); FileSys::PatchManager pm{title_id}; const auto res = pm.GetControlMetadata(); @@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(aoc_change_event.readable); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<AOC_U>()->InstallAsService(service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<AOC_U>(system)->InstallAsService(service_manager); } } // namespace Service::AOC diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 5effea730..848b2f416 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -14,7 +14,7 @@ namespace Service::AOC { class AOC_U final : public ServiceFramework<AOC_U> { public: - AOC_U(); + explicit AOC_U(Core::System& system); ~AOC_U() override; private: @@ -26,9 +26,10 @@ private: std::vector<u64> add_on_content; Kernel::EventPair aoc_change_event; + Core::System& system; }; /// Registers all AOC services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::AOC diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index f3c09bbb1..85bbf5988 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -2,7 +2,6 @@ // 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/interface.h" @@ -12,11 +11,15 @@ namespace Service::APM { Module::Module() = default; Module::~Module() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto module_ = std::make_shared<Module>(); - std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); - std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); - std::make_shared<APM_Sys>()->InstallAsService(service_manager); + std::make_shared<APM>(module_, system.GetAPMController(), "apm") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM>(module_, system.GetAPMController(), "apm:p") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM>(module_, system.GetAPMController(), "apm:am") + ->InstallAsService(system.ServiceManager()); + std::make_shared<APM_Sys>(system.GetAPMController())->InstallAsService(system.ServiceManager()); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h index 4d7d5bb7c..cf4c2bb11 100644 --- a/src/core/hle/service/apm/apm.h +++ b/src/core/hle/service/apm/apm.h @@ -8,11 +8,6 @@ namespace Service::APM { -enum class PerformanceMode : u8 { - Handheld = 0, - Docked = 1, -}; - class Module final { public: Module(); @@ -20,6 +15,6 @@ public: }; /// Registers all AM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::APM diff --git a/src/core/hle/service/apm/controller.cpp b/src/core/hle/service/apm/controller.cpp new file mode 100644 index 000000000..4376612eb --- /dev/null +++ b/src/core/hle/service/apm/controller.cpp @@ -0,0 +1,68 @@ +// Copyright 2019 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_timing.h" +#include "core/hle/service/apm/controller.h" +#include "core/settings.h" + +namespace Service::APM { + +constexpr PerformanceConfiguration 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 const std::map<PerformanceConfiguration, u32> PCONFIG_TO_SPEED_MAP{ + {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}, + }; + + SetClockSpeed(PCONFIG_TO_SPEED_MAP.find(config)->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() { + return Settings::values.use_docked_mode ? 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. +} + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/controller.h b/src/core/hle/service/apm/controller.h new file mode 100644 index 000000000..8ac80eaea --- /dev/null +++ b/src/core/hle/service/apm/controller.h @@ -0,0 +1,70 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include "common/common_types.h" + +namespace Core::Timing { +class CoreTiming; +} + +namespace Service::APM { + +enum class PerformanceConfiguration : u32 { + Config1 = 0x00010000, + Config2 = 0x00010001, + Config3 = 0x00010002, + Config4 = 0x00020000, + Config5 = 0x00020001, + Config6 = 0x00020002, + Config7 = 0x00020003, + Config8 = 0x00020004, + Config9 = 0x00020005, + Config10 = 0x00020006, + Config11 = 0x92220007, + Config12 = 0x92220008, + Config13 = 0x92220009, + Config14 = 0x9222000A, + Config15 = 0x9222000B, + Config16 = 0x9222000C, +}; + +enum class CpuBoostMode : u32 { + Disabled = 0, + Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16 + Partial = 2, // GPU Only -> Config 15 or 16 +}; + +enum class PerformanceMode : u8 { + Handheld = 0, + Docked = 1, +}; + +// Class to manage the state and change of the emulated system performance. +// Specifically, this deals with PerformanceMode, which corresponds to the system being docked or +// undocked, and PerformanceConfig which specifies the exact CPU, GPU, and Memory clocks to operate +// at. Additionally, this manages 'Boost Mode', which allows games to temporarily overclock the +// system during times of high load -- this simply maps to different PerformanceConfigs to use. +class Controller { +public: + Controller(Core::Timing::CoreTiming& core_timing); + ~Controller(); + + void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config); + void SetFromCpuBoostMode(CpuBoostMode mode); + + PerformanceMode GetCurrentPerformanceMode(); + PerformanceConfiguration GetCurrentPerformanceConfiguration(PerformanceMode mode); + +private: + void SetClockSpeed(u32 mhz); + + std::map<PerformanceMode, PerformanceConfiguration> configs; + + Core::Timing::CoreTiming& core_timing; +}; + +} // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index d058c0245..06f0f8edd 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -5,43 +5,32 @@ #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: - ISession() : ServiceFramework("ISession") { + ISession(Controller& controller) : ServiceFramework("ISession"), controller(controller) { static const FunctionInfo functions[] = { {0, &ISession::SetPerformanceConfiguration, "SetPerformanceConfiguration"}, {1, &ISession::GetPerformanceConfiguration, "GetPerformanceConfiguration"}, + {2, nullptr, "SetCpuOverclockEnabled"}, }; RegisterHandlers(functions); } private: - enum class PerformanceConfiguration : u32 { - Config1 = 0x00010000, - Config2 = 0x00010001, - Config3 = 0x00010002, - Config4 = 0x00020000, - Config5 = 0x00020001, - Config6 = 0x00020002, - Config7 = 0x00020003, - Config8 = 0x00020004, - Config9 = 0x00020005, - Config10 = 0x00020006, - Config11 = 0x92220007, - Config12 = 0x92220008, - }; - void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - u32 config = rp.Pop<u32>(); - LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode), - config); + const auto mode = rp.PopEnum<PerformanceMode>(); + const auto config = rp.PopEnum<PerformanceConfiguration>(); + LOG_DEBUG(Service_APM, "called mode={} config={}", static_cast<u32>(mode), + static_cast<u32>(config)); + + controller.SetPerformanceConfiguration(mode, config); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -50,20 +39,23 @@ private: void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto mode = static_cast<PerformanceMode>(rp.Pop<u32>()); - LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); + const auto mode = rp.PopEnum<PerformanceMode>(); + LOG_DEBUG(Service_APM, "called mode={}", static_cast<u32>(mode)); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1)); + rb.PushEnum(controller.GetCurrentPerformanceConfiguration(mode)); } + + Controller& controller; }; -APM::APM(std::shared_ptr<Module> apm, const char* name) - : ServiceFramework(name), apm(std::move(apm)) { +APM::APM(std::shared_ptr<Module> apm, Controller& controller, const char* name) + : ServiceFramework(name), apm(std::move(apm)), controller(controller) { static const FunctionInfo functions[] = { {0, &APM::OpenSession, "OpenSession"}, - {1, nullptr, "GetPerformanceMode"}, + {1, &APM::GetPerformanceMode, "GetPerformanceMode"}, + {6, nullptr, "IsCpuOverclockEnabled"}, }; RegisterHandlers(functions); } @@ -75,10 +67,17 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + rb.PushIpcInterface<ISession>(controller); +} + +void APM::GetPerformanceMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.PushEnum(controller.GetCurrentPerformanceMode()); } -APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { +APM_Sys::APM_Sys(Controller& controller) : ServiceFramework{"apm:sys"}, controller(controller) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "RequestPerformanceMode"}, @@ -87,8 +86,8 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { {3, nullptr, "GetLastThrottlingState"}, {4, nullptr, "ClearLastThrottlingState"}, {5, nullptr, "LoadAndApplySettings"}, - {6, nullptr, "SetCpuBoostMode"}, - {7, nullptr, "GetCurrentPerformanceConfiguration"}, + {6, &APM_Sys::SetCpuBoostMode, "SetCpuBoostMode"}, + {7, &APM_Sys::GetCurrentPerformanceConfiguration, "GetCurrentPerformanceConfiguration"}, }; // clang-format on @@ -102,7 +101,28 @@ void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISession>(); + rb.PushIpcInterface<ISession>(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}", static_cast<u32>(mode)); + + controller.SetFromCpuBoostMode(mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void APM_Sys::GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_APM, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + 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/interface.h index 773541aa4..de1b89437 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -8,24 +8,34 @@ namespace Service::APM { +class Controller; +class Module; + class APM final : public ServiceFramework<APM> { public: - explicit APM(std::shared_ptr<Module> apm, const char* name); + explicit APM(std::shared_ptr<Module> apm, Controller& controller, const char* name); ~APM() override; private: void OpenSession(Kernel::HLERequestContext& ctx); + void GetPerformanceMode(Kernel::HLERequestContext& ctx); std::shared_ptr<Module> apm; + Controller& controller; }; class APM_Sys final : public ServiceFramework<APM_Sys> { public: - explicit APM_Sys(); + explicit APM_Sys(Controller& controller); ~APM_Sys() override; + void SetCpuBoostMode(Kernel::HLERequestContext& ctx); + private: void GetPerformanceEvent(Kernel::HLERequestContext& ctx); + void GetCurrentPerformanceConfiguration(Kernel::HLERequestContext& ctx); + + Controller& controller; }; } // namespace Service::APM diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp deleted file mode 100644 index e675b0188..000000000 --- a/src/core/hle/service/arp/arp.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <memory> - -#include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/service/arp/arp.h" -#include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h" - -namespace Service::ARP { - -class ARP_R final : public ServiceFramework<ARP_R> { -public: - explicit ARP_R() : ServiceFramework{"arp:r"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "GetApplicationLaunchProperty"}, - {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"}, - {2, nullptr, "GetApplicationControlProperty"}, - {3, nullptr, "GetApplicationControlPropertyWithApplicationId"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class IRegistrar final : public ServiceFramework<IRegistrar> { -public: - explicit IRegistrar() : ServiceFramework{"IRegistrar"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "Issue"}, - {1, nullptr, "SetApplicationLaunchProperty"}, - {2, nullptr, "SetApplicationControlProperty"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class ARP_W final : public ServiceFramework<ARP_W> { -public: - explicit ARP_W() : ServiceFramework{"arp:w"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"}, - {1, nullptr, "DeleteProperties"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void AcquireRegistrar(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_ARP, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IRegistrar>(); - } -}; - -void InstallInterfaces(SM::ServiceManager& sm) { - std::make_shared<ARP_R>()->InstallAsService(sm); - std::make_shared<ARP_W>()->InstallAsService(sm); -} - -} // namespace Service::ARP diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h deleted file mode 100644 index 9d100187c..000000000 --- a/src/core/hle/service/arp/arp.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -namespace Service::SM { -class ServiceManager; -} - -namespace Service::ARP { - -/// Registers all ARP services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& sm); - -} // namespace Service::ARP diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index 128df7db5..1781bec83 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -19,16 +19,16 @@ namespace Service::Audio { -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { std::make_shared<AudCtl>()->InstallAsService(service_manager); std::make_shared<AudOutA>()->InstallAsService(service_manager); - std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudOutU>(system)->InstallAsService(service_manager); std::make_shared<AudInA>()->InstallAsService(service_manager); std::make_shared<AudInU>()->InstallAsService(service_manager); std::make_shared<AudRecA>()->InstallAsService(service_manager); std::make_shared<AudRecU>()->InstallAsService(service_manager); std::make_shared<AudRenA>()->InstallAsService(service_manager); - std::make_shared<AudRenU>()->InstallAsService(service_manager); + std::make_shared<AudRenU>(system)->InstallAsService(service_manager); std::make_shared<CodecCtl>()->InstallAsService(service_manager); std::make_shared<HwOpus>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h index f5bd3bf5f..b6d13912e 100644 --- a/src/core/hle/service/audio/audio.h +++ b/src/core/hle/service/audio/audio.h @@ -4,6 +4,10 @@ #pragma once +namespace Core { +class System; +} + namespace Service::SM { class ServiceManager; } @@ -11,6 +15,6 @@ class ServiceManager; namespace Service::Audio { /// Registers all Audio services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 6ba41b20a..fb84a8f13 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -40,8 +40,8 @@ enum class AudioState : u32 { class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name, - std::string&& unique_name) + IAudioOut(Core::System& system, AudoutParams audio_params, AudioCore::AudioOut& audio_core, + std::string&& device_name, std::string&& unique_name) : ServiceFramework("IAudioOut"), audio_core(audio_core), device_name(std::move(device_name)), audio_params(audio_params) { // clang-format off @@ -58,14 +58,13 @@ public: {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, nullptr, "GetAudioOutPlayedSampleCount"}, {11, nullptr, "FlushAudioOutBuffers"}, - {12, nullptr, "SetAudioOutVolume"}, - {13, nullptr, "GetAudioOutVolume"}, + {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"}, + {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"}, }; // clang-format on RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - auto& system = Core::System::GetInstance(); buffer_event = Kernel::WritableEvent::CreateEventPair( system.Kernel(), Kernel::ResetType::Manual, "IAudioOutBufferReleased"); @@ -183,6 +182,25 @@ private: rb.Push(static_cast<u32>(stream->GetQueueSize())); } + void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const float volume = rp.Pop<float>(); + LOG_DEBUG(Service_Audio, "called, volume={}", volume); + + stream->SetVolume(volume); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetAudioOutVolume(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(stream->GetVolume()); + } + AudioCore::AudioOut& audio_core; AudioCore::StreamPtr stream; std::string device_name; @@ -193,6 +211,22 @@ private: Kernel::EventPair buffer_event; }; +AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, + {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, + {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, + {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}, + }; + // clang-format on + + RegisterHandlers(functions); + audio_core = std::make_unique<AudioCore::AudioOut>(); +} + +AudOutU::~AudOutU() = default; + void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); @@ -229,7 +263,7 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())}; auto audio_out_interface = std::make_shared<IAudioOut>( - params, *audio_core, std::move(device_name), std::move(unique_name)); + system, params, *audio_core, std::move(device_name), std::move(unique_name)); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -237,20 +271,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { rb.Push<u32>(params.channel_count); rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16)); rb.Push<u32>(static_cast<u32>(AudioState::Stopped)); - rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface); + rb.PushIpcInterface<IAudioOut>(audio_out_interface); audio_out_interfaces.push_back(std::move(audio_out_interface)); } -AudOutU::AudOutU() : ServiceFramework("audout:u") { - static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, - {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, - {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, - {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; - RegisterHandlers(functions); - audio_core = std::make_unique<AudioCore::AudioOut>(); -} - -AudOutU::~AudOutU() = default; - } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index aed4c43b2..c9f532ccd 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -11,6 +11,10 @@ namespace AudioCore { class AudioOut; } +namespace Core { +class System; +} + namespace Kernel { class HLERequestContext; } @@ -21,15 +25,17 @@ class IAudioOut; class AudOutU final : public ServiceFramework<AudOutU> { public: - AudOutU(); + explicit AudOutU(Core::System& system_); ~AudOutU() override; private: + void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); + void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces; std::unique_ptr<AudioCore::AudioOut> audio_core; - void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); - void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); + Core::System& system; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 75db0c2dc..f162249ed 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -5,6 +5,7 @@ #include <algorithm> #include <array> #include <memory> +#include <string_view> #include "audio_core/audio_renderer.h" #include "common/alignment.h" @@ -25,7 +26,8 @@ namespace Service::Audio { class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) + explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, + const std::size_t instance_number) : ServiceFramework("IAudioRenderer") { // clang-format off static const FunctionInfo functions[] = { @@ -45,11 +47,10 @@ public: // clang-format on RegisterHandlers(functions); - auto& system = Core::System::GetInstance(); system_event = Kernel::WritableEvent::CreateEventPair( system.Kernel(), Kernel::ResetType::Manual, "IAudioRenderer:SystemEvent"); - renderer = std::make_unique<AudioCore::AudioRenderer>(system.CoreTiming(), audren_params, - system_event.writable); + renderer = std::make_unique<AudioCore::AudioRenderer>( + system.CoreTiming(), audren_params, system_event.writable, instance_number); } private: @@ -159,40 +160,81 @@ private: class IAudioDevice final : public ServiceFramework<IAudioDevice> { public: - IAudioDevice() : ServiceFramework("IAudioDevice") { + explicit IAudioDevice(Core::System& system, u32_le revision_num) + : ServiceFramework("IAudioDevice"), revision{revision_num} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, {1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"}, - {2, nullptr, "GetAudioDeviceOutputVolume"}, + {2, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolume"}, {3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"}, {4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"}, {5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"}, - {6, &IAudioDevice::ListAudioDeviceName, - "ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto + {6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"}, {7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"}, - {8, nullptr, "GetAudioDeviceOutputVolumeAuto"}, + {8, &IAudioDevice::GetAudioDeviceOutputVolume, "GetAudioDeviceOutputVolumeAuto"}, {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, - {11, nullptr, "QueryAudioDeviceInputEvent"}, - {12, nullptr, "QueryAudioDeviceOutputEvent"}, + {11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"}, + {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"}, {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, "IAudioOutBufferReleasedEvent"); + + // Should be similar to audio_output_device_switch_event + audio_input_device_switch_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioInputDeviceSwitchedEvent"); + + // Should only be signalled when an audio output device has been changed, example: speaker + // to headset + audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent"); } private: + using AudioDeviceName = std::array<char, 256>; + static constexpr std::array<std::string_view, 4> audio_device_names{{ + "AudioStereoJackOutput", + "AudioBuiltInSpeakerOutput", + "AudioTvOutput", + "AudioUsbDeviceOutput", + }}; + enum class DeviceType { + AHUBHeadphones, + AHUBSpeakers, + HDA, + USBOutput, + }; + void ListAudioDeviceName(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); + + const bool usb_output_supported = + IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision); + const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName); - constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; - ctx.WriteBuffer(audio_interface); + std::vector<AudioDeviceName> name_buffer; + name_buffer.reserve(audio_device_names.size()); + + for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) { + const auto type = static_cast<DeviceType>(i); + + if (!usb_output_supported && type == DeviceType::USBOutput) { + continue; + } + + const auto& device_name = audio_device_names[i]; + auto& entry = name_buffer.emplace_back(); + device_name.copy(entry.data(), device_name.size()); + } + + ctx.WriteBuffer(name_buffer); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); + rb.Push(static_cast<u32>(name_buffer.size())); } void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { @@ -208,15 +250,32 @@ private: rb.Push(RESULT_SUCCESS); } + void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + const auto device_name_buffer = ctx.ReadBuffer(); + const std::string name = Common::StringFromBuffer(device_name_buffer); + + LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(1.0f); + } + void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Audio, "(STUBBED) called"); - constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; - ctx.WriteBuffer(audio_interface); + // Currently set to always be TV audio output. + const auto& device_name = audio_device_names[2]; - IPC::ResponseBuilder rb{ctx, 3}; + AudioDeviceName out_device_name{}; + device_name.copy(out_device_name.data(), device_name.size()); + + ctx.WriteBuffer(out_device_name); + + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(1); } void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) { @@ -237,11 +296,31 @@ private: rb.Push<u32>(1); } + // Should be similar to QueryAudioDeviceOutputEvent + void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_Audio, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(audio_input_device_switch_event.readable); + } + + void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(audio_output_device_switch_event.readable); + } + + u32_le revision = 0; Kernel::EventPair buffer_event; + Kernel::EventPair audio_input_device_switch_event; + Kernel::EventPair audio_output_device_switch_event; }; // namespace Audio -AudRenU::AudRenU() : ServiceFramework("audren:u") { +AudRenU::AudRenU(Core::System& system_) : ServiceFramework("audren:u"), system{system_} { // clang-format off static const FunctionInfo functions[] = { {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, @@ -314,7 +393,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the portion of the size related to the mix data (and the sorting thereof). - const auto calculate_mix_info_size = [this](const AudioCore::AudioRendererParameter& params) { + const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { // The size of the mixing info data structure. constexpr u64 mix_info_size = 0x940; @@ -386,7 +465,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { // Calculates the part of the size related to the splitter context. const auto calculate_splitter_context_size = - [this](const AudioCore::AudioRendererParameter& params) -> u64 { + [](const AudioCore::AudioRendererParameter& params) -> u64 { if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { return 0; } @@ -433,7 +512,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size related to performance statistics. - const auto calculate_perf_size = [this](const AudioCore::AudioRendererParameter& params) { + const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { // Extra size value appended to the end of the calculation. constexpr u64 appended = 128; @@ -460,78 +539,76 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { }; // Calculates the part of the size that relates to the audio command buffer. - const auto calculate_command_buffer_size = - [this](const AudioCore::AudioRendererParameter& params) { - constexpr u64 alignment = (buffer_alignment_size - 1) * 2; + const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { + constexpr u64 alignment = (buffer_alignment_size - 1) * 2; - if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { - constexpr u64 command_buffer_size = 0x18000; + if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { + constexpr u64 command_buffer_size = 0x18000; - return command_buffer_size + alignment; - } + return command_buffer_size + alignment; + } - // When the variadic command buffer is supported, this means - // the command generator for the audio renderer can issue commands - // that are (as one would expect), variable in size. So what we need to do - // is determine the maximum possible size for a few command data structures - // then multiply them by the amount of present commands indicated by the given - // respective audio parameters. + // When the variadic command buffer is supported, this means + // the command generator for the audio renderer can issue commands + // that are (as one would expect), variable in size. So what we need to do + // is determine the maximum possible size for a few command data structures + // then multiply them by the amount of present commands indicated by the given + // respective audio parameters. - constexpr u64 max_biquad_filters = 2; - constexpr u64 max_mix_buffers = 24; + constexpr u64 max_biquad_filters = 2; + constexpr u64 max_mix_buffers = 24; - constexpr u64 biquad_filter_command_size = 0x2C; + constexpr u64 biquad_filter_command_size = 0x2C; - constexpr u64 depop_mix_command_size = 0x24; - constexpr u64 depop_setup_command_size = 0x50; + constexpr u64 depop_mix_command_size = 0x24; + constexpr u64 depop_setup_command_size = 0x50; - constexpr u64 effect_command_max_size = 0x540; + constexpr u64 effect_command_max_size = 0x540; - constexpr u64 mix_command_size = 0x1C; - constexpr u64 mix_ramp_command_size = 0x24; - constexpr u64 mix_ramp_grouped_command_size = 0x13C; + constexpr u64 mix_command_size = 0x1C; + constexpr u64 mix_ramp_command_size = 0x24; + constexpr u64 mix_ramp_grouped_command_size = 0x13C; - constexpr u64 perf_command_size = 0x28; + constexpr u64 perf_command_size = 0x28; - constexpr u64 sink_command_size = 0x130; + constexpr u64 sink_command_size = 0x130; - constexpr u64 submix_command_max_size = - depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; + constexpr u64 submix_command_max_size = + depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; - constexpr u64 volume_command_size = 0x1C; - constexpr u64 volume_ramp_command_size = 0x20; + constexpr u64 volume_command_size = 0x1C; + constexpr u64 volume_ramp_command_size = 0x20; - constexpr u64 voice_biquad_filter_command_size = - biquad_filter_command_size * max_biquad_filters; - constexpr u64 voice_data_command_size = 0x9C; - const u64 voice_command_max_size = - (params.splitter_count * depop_setup_command_size) + - (voice_data_command_size + voice_biquad_filter_command_size + - volume_ramp_command_size + mix_ramp_grouped_command_size); + constexpr u64 voice_biquad_filter_command_size = + biquad_filter_command_size * max_biquad_filters; + constexpr u64 voice_data_command_size = 0x9C; + const u64 voice_command_max_size = + (params.splitter_count * depop_setup_command_size) + + (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + + mix_ramp_grouped_command_size); - // Now calculate the individual elements that comprise the size and add them together. - const u64 effect_commands_size = params.effect_count * effect_command_max_size; + // Now calculate the individual elements that comprise the size and add them together. + const u64 effect_commands_size = params.effect_count * effect_command_max_size; - const u64 final_mix_commands_size = - depop_mix_command_size + volume_command_size * max_mix_buffers; + const u64 final_mix_commands_size = + depop_mix_command_size + volume_command_size * max_mix_buffers; - const u64 perf_commands_size = - perf_command_size * - (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); + const u64 perf_commands_size = + perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); - const u64 sink_commands_size = params.sink_count * sink_command_size; + const u64 sink_commands_size = params.sink_count * sink_command_size; - const u64 splitter_commands_size = - params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; + const u64 splitter_commands_size = + params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; - const u64 submix_commands_size = params.submix_count * submix_command_max_size; + const u64 submix_commands_size = params.submix_count * submix_command_max_size; - const u64 voice_commands_size = params.voice_count * voice_command_max_size; + const u64 voice_commands_size = params.voice_count * voice_command_max_size; - return effect_commands_size + final_mix_commands_size + perf_commands_size + - sink_commands_size + splitter_commands_size + submix_commands_size + - voice_commands_size + alignment; - }; + return effect_commands_size + final_mix_commands_size + perf_commands_size + + sink_commands_size + splitter_commands_size + submix_commands_size + + voice_commands_size + alignment; + }; IPC::RequestParser rp{ctx}; const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); @@ -564,12 +641,16 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { } void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; + const u64 aruid = rp.Pop<u64>(); - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid); + // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that + // always assumes the initial release revision (REV1). + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioDevice>(); + rb.PushIpcInterface<IAudioDevice>(system, Common::MakeMagic('R', 'E', 'V', '1')); } void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { @@ -579,13 +660,19 @@ void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) { } void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + struct Parameters { + u32 revision; + u64 aruid; + }; - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + IPC::RequestParser rp{ctx}; + const auto [revision, aruid] = rp.PopRaw<Parameters>(); + + LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid); + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different - // based on the current revision + rb.PushIpcInterface<IAudioDevice>(system, revision); } void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { @@ -594,14 +681,16 @@ void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IAudioRenderer>(params); + rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++); } -bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { +bool IsFeatureSupported(AudioFeatures feature, u32_le revision) { // Byte swap const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0'); switch (feature) { + case AudioFeatures::AudioUSBDeviceOutput: + return version_num >= 4U; case AudioFeatures::Splitter: return version_num >= 2U; case AudioFeatures::PerformanceMetricsVersion2: diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index 1d3c8df61..4e0ccc792 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -6,6 +6,10 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Kernel { class HLERequestContext; } @@ -14,7 +18,7 @@ namespace Service::Audio { class AudRenU final : public ServiceFramework<AudRenU> { public: - explicit AudRenU(); + explicit AudRenU(Core::System& system_); ~AudRenU() override; private: @@ -26,13 +30,19 @@ private: void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx); - enum class AudioFeatures : u32 { - Splitter, - PerformanceMetricsVersion2, - VariadicCommandBuffer, - }; + std::size_t audren_instance_count = 0; + Core::System& system; +}; - bool IsFeatureSupported(AudioFeatures feature, u32_le revision) const; +// Describes a particular audio feature that may be supported in a particular revision. +enum class AudioFeatures : u32 { + AudioUSBDeviceOutput, + Splitter, + PerformanceMetricsVersion2, + VariadicCommandBuffer, }; +// Tests if a particular audio feature is supported with a given audio revision. +bool IsFeatureSupported(AudioFeatures feature, u32_le revision); + } // namespace Service::Audio diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp index 3c7ca2c44..afce581e5 100644 --- a/src/core/hle/service/btdrv/btdrv.cpp +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -16,7 +16,7 @@ namespace Service::BtDrv { class Bt final : public ServiceFramework<Bt> { public: - explicit Bt() : ServiceFramework{"bt"} { + explicit Bt(Core::System& system) : ServiceFramework{"bt"} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LeClientReadCharacteristic"}, @@ -33,7 +33,7 @@ public: // clang-format on RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); register_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); } @@ -163,9 +163,9 @@ public: } }; -void InstallInterfaces(SM::ServiceManager& sm) { +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<BtDrv>()->InstallAsService(sm); - std::make_shared<Bt>()->InstallAsService(sm); + std::make_shared<Bt>(system)->InstallAsService(sm); } } // namespace Service::BtDrv diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h index 164e56f43..191410dbc 100644 --- a/src/core/hle/service/btdrv/btdrv.h +++ b/src/core/hle/service/btdrv/btdrv.h @@ -8,9 +8,13 @@ namespace Service::SM { class ServiceManager; } +namespace Core { +class System; +} + namespace Service::BtDrv { /// Registers all BtDrv services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& sm); +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::BtDrv diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index b439ee7ec..920fc6ff7 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -17,7 +17,7 @@ namespace Service::BTM { class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { public: - explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { + explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} { // clang-format off static const FunctionInfo functions[] = { {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, @@ -56,7 +56,7 @@ public: // clang-format on RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, "IBtmUserCore:ScanEvent"); connection_event = Kernel::WritableEvent::CreateEventPair( @@ -108,7 +108,7 @@ private: class BTM_USR final : public ServiceFramework<BTM_USR> { public: - explicit BTM_USR() : ServiceFramework{"btm:u"} { + explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &BTM_USR::GetCore, "GetCore"}, @@ -123,8 +123,10 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IBtmUserCore>(); + rb.PushIpcInterface<IBtmUserCore>(system); } + + Core::System& system; }; class BTM final : public ServiceFramework<BTM> { @@ -268,11 +270,11 @@ private: } }; -void InstallInterfaces(SM::ServiceManager& sm) { +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<BTM>()->InstallAsService(sm); std::make_shared<BTM_DBG>()->InstallAsService(sm); std::make_shared<BTM_SYS>()->InstallAsService(sm); - std::make_shared<BTM_USR>()->InstallAsService(sm); + std::make_shared<BTM_USR>(system)->InstallAsService(sm); } } // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h index e6425a7e3..c6b878043 100644 --- a/src/core/hle/service/btm/btm.h +++ b/src/core/hle/service/btm/btm.h @@ -8,8 +8,12 @@ namespace Service::SM { class ServiceManager; } +namespace Core { +class System; +}; + namespace Service::BTM { -void InstallInterfaces(SM::ServiceManager& sm); +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::BTM diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 6701cb913..af70d174d 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -2,32 +2,37 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/crypto/key_manager.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/service/service.h" namespace Service::ES { +constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; +constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; + class ETicket final : public ServiceFramework<ETicket> { public: explicit ETicket() : ServiceFramework{"es"} { // clang-format off static const FunctionInfo functions[] = { - {1, nullptr, "ImportTicket"}, + {1, &ETicket::ImportTicket, "ImportTicket"}, {2, nullptr, "ImportTicketCertificateSet"}, {3, nullptr, "DeleteTicket"}, {4, nullptr, "DeletePersonalizedTicket"}, {5, nullptr, "DeleteAllCommonTicket"}, {6, nullptr, "DeleteAllPersonalizedTicket"}, {7, nullptr, "DeleteAllPersonalizedTicketEx"}, - {8, nullptr, "GetTitleKey"}, - {9, nullptr, "CountCommonTicket"}, - {10, nullptr, "CountPersonalizedTicket"}, - {11, nullptr, "ListCommonTicket"}, - {12, nullptr, "ListPersonalizedTicket"}, + {8, &ETicket::GetTitleKey, "GetTitleKey"}, + {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, + {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, + {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, + {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, {13, nullptr, "ListMissingPersonalizedTicket"}, - {14, nullptr, "GetCommonTicketSize"}, - {15, nullptr, "GetPersonalizedTicketSize"}, - {16, nullptr, "GetCommonTicketData"}, - {17, nullptr, "GetPersonalizedTicketData"}, + {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, + {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, + {16, &ETicket::GetCommonTicketData, "GetCommonTicketData"}, + {17, &ETicket::GetPersonalizedTicketData, "GetPersonalizedTicketData"}, {18, nullptr, "OwnTicket"}, {19, nullptr, "GetTicketInfo"}, {20, nullptr, "ListLightTicketInfo"}, @@ -51,7 +56,212 @@ public: }; // clang-format on RegisterHandlers(functions); + + keys.PopulateTickets(); + keys.SynthesizeTickets(); + } + +private: + bool CheckRightsId(Kernel::HLERequestContext& ctx, const u128& rights_id) { + if (rights_id == u128{}) { + LOG_ERROR(Service_ETicket, "The rights ID was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_RIGHTS_ID); + return false; + } + + return true; + } + + void ImportTicket(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto ticket = ctx.ReadBuffer(); + const auto cert = ctx.ReadBuffer(1); + + if (ticket.size() < sizeof(Core::Crypto::Ticket)) { + LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + Core::Crypto::Ticket raw{}; + std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); + + if (!keys.AddTicketPersonalized(raw)) { + LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_ARGUMENT); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetTitleKey(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto key = + keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); + + if (key == Core::Crypto::Key128{}) { + LOG_ERROR(Service_ETicket, + "The titlekey doesn't exist in the KeyManager or the rights ID was invalid!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_INVALID_RIGHTS_ID); + return; + } + + ctx.WriteBuffer(key.data(), key.size()); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void CountCommonTicket(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ETicket, "called"); + + const auto count = keys.GetCommonTickets().size(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(count); + } + + void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ETicket, "called"); + + const auto count = keys.GetPersonalizedTickets().size(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(count); + } + + void ListCommonTicket(Kernel::HLERequestContext& ctx) { + u32 out_entries; + if (keys.GetCommonTickets().empty()) + out_entries = 0; + else + out_entries = ctx.GetWriteBufferSize() / sizeof(u128); + + LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); + + keys.PopulateTickets(); + const auto tickets = keys.GetCommonTickets(); + std::vector<u128> ids; + std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), + [](const auto& pair) { return pair.first; }); + + out_entries = std::min<u32>(ids.size(), out_entries); + ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(out_entries); } + + void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { + u32 out_entries; + if (keys.GetPersonalizedTickets().empty()) + out_entries = 0; + else + out_entries = ctx.GetWriteBufferSize() / sizeof(u128); + + LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); + + keys.PopulateTickets(); + const auto tickets = keys.GetPersonalizedTickets(); + std::vector<u128> ids; + std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), + [](const auto& pair) { return pair.first; }); + + out_entries = std::min<u32>(ids.size(), out_entries); + ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(out_entries); + } + + void GetCommonTicketSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetCommonTickets().at(rights_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ticket.GetSize()); + } + + void GetPersonalizedTicketSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetPersonalizedTickets().at(rights_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(ticket.GetSize()); + } + + void GetCommonTicketData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetCommonTickets().at(rights_id); + + const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); + ctx.WriteBuffer(&ticket, write_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(write_size); + } + + void GetPersonalizedTicketData(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto rights_id = rp.PopRaw<u128>(); + + LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); + + if (!CheckRightsId(ctx, rights_id)) + return; + + const auto ticket = keys.GetPersonalizedTickets().at(rights_id); + + const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); + ctx.WriteBuffer(&ticket, write_size); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u64>(write_size); + } + + Core::Crypto::KeyManager keys; }; void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 2c229bcad..b2ebf6240 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp @@ -5,7 +5,7 @@ #include <array> #include <cstring> #include <ctime> -#include <fmt/time.h> +#include <fmt/chrono.h> #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" @@ -16,11 +16,12 @@ #include "core/hle/service/fatal/fatal.h" #include "core/hle/service/fatal/fatal_p.h" #include "core/hle/service/fatal/fatal_u.h" +#include "core/reporter.h" namespace Service::Fatal { -Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} +Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) + : ServiceFramework(name), module(std::move(module)), system(system) {} Module::Interface::~Interface() = default; @@ -63,7 +64,8 @@ enum class FatalType : u32 { ErrorScreen = 2, }; -static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { +static void GenerateErrorReport(Core::System& system, ResultCode error_code, + const FatalInfo& info) { const auto title_id = Core::CurrentProcess()->GetTitleID(); std::string crash_report = fmt::format( "Yuzu {}-{} crash report\n" @@ -100,35 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { LOG_ERROR(Service_Fatal, "{}", crash_report); - const std::string crashreport_dir = - FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; - - if (!FileUtil::CreateFullPath(crashreport_dir)) { - LOG_ERROR( - Service_Fatal, - "Unable to create crash report directory. Possible log directory permissions issue."); - return; - } - - const std::time_t t = std::time(nullptr); - const std::string crashreport_filename = - fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t)); - - auto file = FileUtil::IOFile(crashreport_filename, "wb"); - if (file.IsOpen()) { - file.WriteString(crash_report); - LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename); - } else { - LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename); - } + system.GetReporter().SaveCrashReport( + title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, + info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, + info.backtrace_size, info.ArchAsString(), info.unk10); } -static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { +static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type, + const FatalInfo& info) { LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", static_cast<u32>(fatal_type), error_code.raw); switch (fatal_type) { case FatalType::ErrorReportAndScreen: - GenerateErrorReport(error_code, info); + GenerateErrorReport(system, error_code, info); [[fallthrough]]; case FatalType::ErrorScreen: // Since we have no fatal:u error screen. We should just kill execution instead @@ -136,7 +122,7 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F break; // Should not throw a fatal screen but should generate an error report case FatalType::ErrorReport: - GenerateErrorReport(error_code, info); + GenerateErrorReport(system, error_code, info); break; } } @@ -146,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto error_code = rp.Pop<ResultCode>(); - ThrowFatalError(error_code, FatalType::ErrorScreen, {}); + ThrowFatalError(system, error_code, FatalType::ErrorScreen, {}); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -157,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { const auto error_code = rp.Pop<ResultCode>(); const auto fatal_type = rp.PopEnum<FatalType>(); - ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy + ThrowFatalError(system, error_code, fatal_type, + {}); // No info is passed with ThrowFatalWithPolicy IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -173,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); - ThrowFatalError(error_code, fatal_type, info); + ThrowFatalError(system, error_code, fatal_type, info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { auto module = std::make_shared<Module>(); - std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); - std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); + std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager); + std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager); } } // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 09371ff7f..bd9339dfc 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h @@ -6,13 +6,17 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::Fatal { class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(std::shared_ptr<Module> module, const char* name); + explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); ~Interface() override; void ThrowFatal(Kernel::HLERequestContext& ctx); @@ -21,9 +25,10 @@ public: protected: std::shared_ptr<Module> module; + Core::System& system; }; }; -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::Fatal diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp index 9e5f872ff..066ccf6b0 100644 --- a/src/core/hle/service/fatal/fatal_p.cpp +++ b/src/core/hle/service/fatal/fatal_p.cpp @@ -6,8 +6,8 @@ namespace Service::Fatal { -Fatal_P::Fatal_P(std::shared_ptr<Module> module) - : Module::Interface(std::move(module), "fatal:p") {} +Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system) + : Module::Interface(std::move(module), system, "fatal:p") {} Fatal_P::~Fatal_P() = default; diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h index 6e9c5979f..c6d953cb5 100644 --- a/src/core/hle/service/fatal/fatal_p.h +++ b/src/core/hle/service/fatal/fatal_p.h @@ -10,7 +10,7 @@ namespace Service::Fatal { class Fatal_P final : public Module::Interface { public: - explicit Fatal_P(std::shared_ptr<Module> module); + explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system); ~Fatal_P() override; }; diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index 1572a2051..8d72ed485 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp @@ -6,7 +6,8 @@ namespace Service::Fatal { -Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { +Fatal_U::Fatal_U(std::shared_ptr<Module> module, Core::System& system) + : Module::Interface(std::move(module), system, "fatal:u") { static const FunctionInfo functions[] = { {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h index 72cb6d076..34c5c7f95 100644 --- a/src/core/hle/service/fatal/fatal_u.h +++ b/src/core/hle/service/fatal/fatal_u.h @@ -10,7 +10,7 @@ namespace Service::Fatal { class Fatal_U final : public Module::Interface { public: - explicit Fatal_U(std::shared_ptr<Module> module); + explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system); ~Fatal_U() override; }; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 1ebfeb4bf..14cd0e322 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -8,6 +8,7 @@ #include "common/file_util.h" #include "core/core.h" #include "core/file_sys/bis_factory.h" +#include "core/file_sys/card_image.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/errors.h" #include "core/file_sys/mode.h" @@ -25,14 +26,10 @@ #include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" #include "core/loader/loader.h" +#include "core/settings.h" namespace Service::FileSystem { -// Size of emulated sd card free space, reported in bytes. -// Just using 32GB because thats reasonable -// TODO(DarkLordZach): Eventually make this configurable in settings. -constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; - // A default size for normal/journal save data size if application control metadata cannot be found. // This should be large enough to satisfy even the most extreme requirements (~4.2GB) constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; @@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s return MakeResult(dir); } -u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { - if (backing->IsWritable()) - return EMULATED_SD_REPORTED_SIZE; - - return 0; -} - ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( const std::string& path_) const { std::string path(FileUtil::SanitizePath(path_)); @@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( return FileSys::ERROR_PATH_NOT_FOUND; } -/** - * Map of registered file systems, identified by type. Once an file system is registered here, it - * is never removed until UnregisterFileSystems is called. - */ -static std::unique_ptr<FileSys::RomFSFactory> romfs_factory; -static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; -static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; -static std::unique_ptr<FileSys::BISFactory> bis_factory; +FileSystemController::FileSystemController() = default; + +FileSystemController::~FileSystemController() = default; -ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { - ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS"); +ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { romfs_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered RomFS"); return RESULT_SUCCESS; } -ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { - ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data"); +ResultCode FileSystemController::RegisterSaveData( + std::unique_ptr<FileSys::SaveDataFactory>&& factory) { + ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data"); save_data_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered save data"); return RESULT_SUCCESS; } -ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { +ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); sdmc_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered SDMC"); return RESULT_SUCCESS; } -ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { +ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); bis_factory = std::move(factory); LOG_DEBUG(Service_FS, "Registered BIS"); return RESULT_SUCCESS; } -void SetPackedUpdate(FileSys::VirtualFile update_raw) { +void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) { LOG_TRACE(Service_FS, "Setting packed update for romfs"); if (romfs_factory == nullptr) @@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) { romfs_factory->SetPackedUpdate(std::move(update_raw)); } -ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { +ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const { LOG_TRACE(Service_FS, "Opening RomFS for current process"); if (romfs_factory == nullptr) { @@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { return romfs_factory->OpenCurrentProcess(); } -ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type) { +ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( + u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const { LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); @@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora return romfs_factory->Open(title_id, storage_id, type); } -ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, - const FileSys::SaveDataDescriptor& descriptor) { +ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( + FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { + LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", + static_cast<u8>(space), save_struct.DebugInfo()); + + if (save_data_factory == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + return save_data_factory->Create(space, save_struct); +} + +ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( + FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", static_cast<u8>(space), descriptor.DebugInfo()); @@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, return save_data_factory->Open(space, descriptor); } -ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { +ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( + FileSys::SaveDataSpaceId space) const { LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); if (save_data_factory == nullptr) { @@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); } -ResultVal<FileSys::VirtualDir> OpenSDMC() { +ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const { LOG_TRACE(Service_FS, "Opening SDMC"); if (sdmc_factory == nullptr) { @@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() { return sdmc_factory->Open(); } -FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { +ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition( + FileSys::BisPartitionId id) const { + LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id)); + + if (bis_factory == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + auto part = bis_factory->OpenPartition(id); + if (part == nullptr) { + return FileSys::ERROR_INVALID_ARGUMENT; + } + + return MakeResult<FileSys::VirtualDir>(std::move(part)); +} + +ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage( + FileSys::BisPartitionId id) const { + LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id)); + + if (bis_factory == nullptr) { + return FileSys::ERROR_ENTITY_NOT_FOUND; + } + + auto part = bis_factory->OpenPartitionStorage(id); + if (part == nullptr) { + return FileSys::ERROR_INVALID_ARGUMENT; + } + + return MakeResult<FileSys::VirtualFile>(std::move(part)); +} + +u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const { + switch (id) { + case FileSys::StorageId::None: + case FileSys::StorageId::GameCard: + return 0; + case FileSys::StorageId::SdCard: + if (sdmc_factory == nullptr) + return 0; + return sdmc_factory->GetSDMCFreeSpace(); + case FileSys::StorageId::Host: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace(); + case FileSys::StorageId::NandSystem: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetSystemNANDFreeSpace(); + case FileSys::StorageId::NandUser: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetUserNANDFreeSpace(); + } + + return 0; +} + +u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const { + switch (id) { + case FileSys::StorageId::None: + case FileSys::StorageId::GameCard: + return 0; + case FileSys::StorageId::SdCard: + if (sdmc_factory == nullptr) + return 0; + return sdmc_factory->GetSDMCTotalSpace(); + case FileSys::StorageId::Host: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetFullNANDTotalSpace(); + case FileSys::StorageId::NandSystem: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetSystemNANDTotalSpace(); + case FileSys::StorageId::NandUser: + if (bis_factory == nullptr) + return 0; + return bis_factory->GetUserNANDTotalSpace(); + } + + return 0; +} + +FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type, + u64 title_id, u128 user_id) const { if (save_data_factory == nullptr) { return {0, 0}; } @@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, return value; } -void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value) { +void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) const { if (save_data_factory != nullptr) save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); } -FileSys::RegisteredCache* GetSystemNANDContents() { +void FileSystemController::SetGameCard(FileSys::VirtualFile file) { + gamecard = std::make_unique<FileSys::XCI>(file); + const auto dir = gamecard->ConcatenatedPseudoDirectory(); + gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir); + gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir); +} + +FileSys::XCI* FileSystemController::GetGameCard() const { + return gamecard.get(); +} + +FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const { + return gamecard_registered.get(); +} + +FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const { + return gamecard_placeholder.get(); +} + +FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const { LOG_TRACE(Service_FS, "Opening System NAND Contents"); if (bis_factory == nullptr) @@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() { return bis_factory->GetSystemNANDContents(); } -FileSys::RegisteredCache* GetUserNANDContents() { +FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const { LOG_TRACE(Service_FS, "Opening User NAND Contents"); if (bis_factory == nullptr) @@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() { return bis_factory->GetUserNANDContents(); } -FileSys::RegisteredCache* GetSDMCContents() { +FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const { LOG_TRACE(Service_FS, "Opening SDMC Contents"); if (sdmc_factory == nullptr) @@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() { return sdmc_factory->GetSDMCContents(); } -FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { +FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const { + LOG_TRACE(Service_FS, "Opening System NAND Placeholder"); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetSystemNANDPlaceholder(); +} + +FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const { + LOG_TRACE(Service_FS, "Opening User NAND Placeholder"); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetUserNANDPlaceholder(); +} + +FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const { + LOG_TRACE(Service_FS, "Opening SDMC Placeholder"); + + if (sdmc_factory == nullptr) + return nullptr; + + return sdmc_factory->GetSDMCPlaceholder(); +} + +FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage( + FileSys::StorageId id) const { + switch (id) { + case FileSys::StorageId::None: + case FileSys::StorageId::Host: + UNIMPLEMENTED(); + return nullptr; + case FileSys::StorageId::GameCard: + return GetGameCardContents(); + case FileSys::StorageId::NandSystem: + return GetSystemNANDContents(); + case FileSys::StorageId::NandUser: + return GetUserNANDContents(); + case FileSys::StorageId::SdCard: + return GetSDMCContents(); + } + + return nullptr; +} + +FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage( + FileSys::StorageId id) const { + switch (id) { + case FileSys::StorageId::None: + case FileSys::StorageId::Host: + UNIMPLEMENTED(); + return nullptr; + case FileSys::StorageId::GameCard: + return GetGameCardPlaceholder(); + case FileSys::StorageId::NandSystem: + return GetSystemNANDPlaceholder(); + case FileSys::StorageId::NandUser: + return GetUserNANDPlaceholder(); + case FileSys::StorageId::SdCard: + return GetSDMCPlaceholder(); + } + + return nullptr; +} + +FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const { + LOG_TRACE(Service_FS, "Opening system NAND content directory"); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetSystemNANDContentDirectory(); +} + +FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const { + LOG_TRACE(Service_FS, "Opening user NAND content directory"); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetUserNANDContentDirectory(); +} + +FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const { + LOG_TRACE(Service_FS, "Opening SDMC content directory"); + + if (sdmc_factory == nullptr) + return nullptr; + + return sdmc_factory->GetSDMCContentDirectory(); +} + +FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const { + LOG_TRACE(Service_FS, "Opening NAND image directory"); + + if (bis_factory == nullptr) + return nullptr; + + return bis_factory->GetImageDirectory(); +} + +FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const { + LOG_TRACE(Service_FS, "Opening SDMC image directory"); + + if (sdmc_factory == nullptr) + return nullptr; + + return sdmc_factory->GetImageDirectory(); +} + +FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const { + switch (id) { + case ContentStorageId::System: + return GetSystemNANDContentDirectory(); + case ContentStorageId::User: + return GetUserNANDContentDirectory(); + case ContentStorageId::SdCard: + return GetSDMCContentDirectory(); + } + + return nullptr; +} + +FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const { + switch (id) { + case ImageDirectoryId::NAND: + return GetNANDImageDirectory(); + case ImageDirectoryId::SdCard: + return GetSDMCImageDirectory(); + } + + return nullptr; +} + +FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const { LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); if (bis_factory == nullptr) @@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { return bis_factory->GetModificationLoadRoot(title_id); } -FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { +FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const { LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); if (bis_factory == nullptr) @@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { return bis_factory->GetModificationDumpRoot(title_id); } -void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { +void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { if (overwrite) { bis_factory = nullptr; save_data_factory = nullptr; @@ -472,12 +710,11 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { } } -void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) { - romfs_factory = nullptr; - CreateFactories(vfs, false); - std::make_shared<FSP_LDR>()->InstallAsService(service_manager); - std::make_shared<FSP_PR>()->InstallAsService(service_manager); - std::make_shared<FSP_SRV>()->InstallAsService(service_manager); +void InstallInterfaces(Core::System& system) { + std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); + std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); + std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter()) + ->InstallAsService(system.ServiceManager()); } } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 6481f237c..3e0c03ec0 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -14,10 +14,13 @@ namespace FileSys { class BISFactory; class RegisteredCache; class RegisteredCacheUnion; +class PlaceholderCache; class RomFSFactory; class SaveDataFactory; class SDMCFactory; +class XCI; +enum class BisPartitionId : u32; enum class ContentRecordType : u8; enum class Mode : u32; enum class SaveDataSpaceId : u8; @@ -36,36 +39,93 @@ class ServiceManager; namespace FileSystem { -ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); -ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); -ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); -ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); - -void SetPackedUpdate(FileSys::VirtualFile update_raw); -ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); -ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, - FileSys::ContentRecordType type); -ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, - const FileSys::SaveDataDescriptor& descriptor); -ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); -ResultVal<FileSys::VirtualDir> OpenSDMC(); - -FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id); -void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, - FileSys::SaveDataSize new_value); +enum class ContentStorageId : u32 { + System, + User, + SdCard, +}; -FileSys::RegisteredCache* GetSystemNANDContents(); -FileSys::RegisteredCache* GetUserNANDContents(); -FileSys::RegisteredCache* GetSDMCContents(); +enum class ImageDirectoryId : u32 { + NAND, + SdCard, +}; -FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); -FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); +class FileSystemController { +public: + FileSystemController(); + ~FileSystemController(); + + ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); + ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); + ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); + ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); + + void SetPackedUpdate(FileSys::VirtualFile update_raw); + ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const; + ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, + FileSys::ContentRecordType type) const; + ResultVal<FileSys::VirtualDir> CreateSaveData( + FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; + ResultVal<FileSys::VirtualDir> OpenSaveData( + FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; + ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; + ResultVal<FileSys::VirtualDir> OpenSDMC() const; + ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; + ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const; + + u64 GetFreeSpaceSize(FileSys::StorageId id) const; + u64 GetTotalSpaceSize(FileSys::StorageId id) const; + + FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, + u128 user_id) const; + void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, + FileSys::SaveDataSize new_value) const; + + void SetGameCard(FileSys::VirtualFile file); + FileSys::XCI* GetGameCard() const; + + FileSys::RegisteredCache* GetSystemNANDContents() const; + FileSys::RegisteredCache* GetUserNANDContents() const; + FileSys::RegisteredCache* GetSDMCContents() const; + FileSys::RegisteredCache* GetGameCardContents() const; + + FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const; + FileSys::PlaceholderCache* GetUserNANDPlaceholder() const; + FileSys::PlaceholderCache* GetSDMCPlaceholder() const; + FileSys::PlaceholderCache* GetGameCardPlaceholder() const; + + FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const; + FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const; + + FileSys::VirtualDir GetSystemNANDContentDirectory() const; + FileSys::VirtualDir GetUserNANDContentDirectory() const; + FileSys::VirtualDir GetSDMCContentDirectory() const; + + FileSys::VirtualDir GetNANDImageDirectory() const; + FileSys::VirtualDir GetSDMCImageDirectory() const; + + FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const; + FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const; + + FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; + FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; + + // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function + // above is called. + void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); -// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function -// above is called. -void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); +private: + std::unique_ptr<FileSys::RomFSFactory> romfs_factory; + std::unique_ptr<FileSys::SaveDataFactory> save_data_factory; + std::unique_ptr<FileSys::SDMCFactory> sdmc_factory; + std::unique_ptr<FileSys::BISFactory> bis_factory; + + std::unique_ptr<FileSys::XCI> gamecard; + std::unique_ptr<FileSys::RegisteredCache> gamecard_registered; + std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder; +}; -void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs); +void InstallInterfaces(Core::System& system); // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of // pointers and booleans. This makes using a VfsDirectory with switch services much easier and @@ -160,12 +220,6 @@ public: ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); /** - * Get the free space - * @return The number of free bytes in the archive - */ - u64 GetFreeSpaceSize() const; - - /** * Get the type of the specified path * @return The type of the specified path or error code */ diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index e7df8fd98..eb982ad49 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -19,6 +19,7 @@ #include "core/file_sys/mode.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/savedata_factory.h" #include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" @@ -26,9 +27,22 @@ #include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/fsp_srv.h" +#include "core/reporter.h" namespace Service::FileSystem { +struct SizeGetter { + std::function<u64()> get_free_size; + std::function<u64()> get_total_size; + + static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) { + return { + [&fsc, id] { return fsc.GetFreeSpaceSize(id); }, + [&fsc, id] { return fsc.GetTotalSpaceSize(id); }, + }; + } +}; + enum class FileSystemType : u8 { Invalid0 = 0, Invalid1 = 1, @@ -288,8 +302,8 @@ private: class IFileSystem final : public ServiceFramework<IFileSystem> { public: - explicit IFileSystem(FileSys::VirtualDir backend) - : ServiceFramework("IFileSystem"), backend(std::move(backend)) { + explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size) + : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) { static const FunctionInfo functions[] = { {0, &IFileSystem::CreateFile, "CreateFile"}, {1, &IFileSystem::DeleteFile, "DeleteFile"}, @@ -466,14 +480,31 @@ public: rb.Push(RESULT_SUCCESS); } + void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(size.get_free_size()); + } + + void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(size.get_total_size()); + } + private: VfsDirectoryServiceWrapper backend; + SizeGetter size; }; class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { public: - explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) - : ServiceFramework("ISaveDataInfoReader") { + explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc) + : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) { static const FunctionInfo functions[] = { {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, }; @@ -519,8 +550,13 @@ private: } void FindAllSaves(FileSys::SaveDataSpaceId space) { - const auto save_root = OpenSaveDataSpace(space); - ASSERT(save_root.Succeeded()); + const auto save_root = fsc.OpenSaveDataSpace(space); + + if (save_root.Failed() || *save_root == nullptr) { + LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!", + static_cast<u8>(space)); + return; + } for (const auto& type : (*save_root)->GetSubdirectories()) { if (type->GetName() == "save") { @@ -609,11 +645,13 @@ private: }; static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); + FileSystemController& fsc; std::vector<SaveDataInfo> info; u64 next_entry_index = 0; }; -FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { +FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) + : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "OpenFileSystem"}, @@ -710,14 +748,14 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {1001, nullptr, "SetSaveDataSize"}, {1002, nullptr, "SetSaveDataRootPath"}, {1003, nullptr, "DisableAutoSaveDataCreation"}, - {1004, nullptr, "SetGlobalAccessLogMode"}, + {1004, &FSP_SRV::SetGlobalAccessLogMode, "SetGlobalAccessLogMode"}, {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, - {1006, nullptr, "OutputAccessLogToSdCard"}, + {1006, &FSP_SRV::OutputAccessLogToSdCard, "OutputAccessLogToSdCard"}, {1007, nullptr, "RegisterUpdatePartition"}, {1008, nullptr, "OpenRegisteredUpdatePartition"}, {1009, nullptr, "GetAndClearMemoryReportInfo"}, {1010, nullptr, "SetDataStorageRedirectTarget"}, - {1011, nullptr, "OutputAccessLogToSdCard2"}, + {1011, &FSP_SRV::GetAccessLogVersionInfo, "GetAccessLogVersionInfo"}, {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, {1110, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId2"}, {1200, nullptr, "OpenMultiCommitManager"}, @@ -753,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - IFileSystem filesystem(OpenSDMC().Unwrap()); + IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), + SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -767,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); u128 uid = rp.PopRaw<u128>(); - LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}", - save_struct.DebugInfo(), uid[1], uid[0]); + LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(), + uid[1], uid[0]); + + fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -785,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto parameters = rp.PopRaw<Parameters>(); - auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); + auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); return; } - IFileSystem filesystem(std::move(dir.Unwrap())); + FileSys::StorageId id; + if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { + id = FileSys::StorageId::NandUser; + } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || + parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { + id = FileSys::StorageId::SdCard; + } else { + id = FileSys::StorageId::NandSystem; + } + + IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -811,30 +862,31 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); + rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); } -void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_FS, "(STUBBED) called"); +void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + log_mode = rp.PopEnum<LogMode>(); - enum class LogMode : u32 { - Off, - Log, - RedirectToSdCard, - LogToSdCard = Log | RedirectToSdCard, - }; + LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); - // Given we always want to receive logging information, - // we always specify logging as enabled. IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.PushEnum(LogMode::Log); + rb.PushEnum(log_mode); } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called"); - auto romfs = OpenRomFSCurrentProcess(); + auto romfs = fsc.OpenRomFSCurrentProcess(); if (romfs.Failed()) { // TODO (bunnei): Find the right error code to use here LOG_CRITICAL(Service_FS, "no file system interface available!"); @@ -859,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", static_cast<u8>(storage_id), unknown, title_id); - auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); + auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (data.Failed()) { const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); @@ -902,4 +954,26 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } +void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { + const auto raw = ctx.ReadBuffer(); + auto log = Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast<const char*>(raw.data()), raw.size()); + + LOG_DEBUG(Service_FS, "called, log='{}'", log); + + reporter.SaveFilesystemAccessReport(log_mode, std::move(log)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(AccessLogVersion::Latest); + rb.Push(access_log_program_index); +} + } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index d7572ba7a..d52b55999 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -7,15 +7,32 @@ #include <memory> #include "core/hle/service/service.h" +namespace Core { +class Reporter; +} + namespace FileSys { class FileSystemBackend; } namespace Service::FileSystem { +enum class AccessLogVersion : u32 { + V7_0_0 = 2, + + Latest = V7_0_0, +}; + +enum class LogMode : u32 { + Off, + Log, + RedirectToSdCard, + LogToSdCard = Log | RedirectToSdCard, +}; + class FSP_SRV final : public ServiceFramework<FSP_SRV> { public: - explicit FSP_SRV(); + explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter); ~FSP_SRV() override; private: @@ -26,13 +43,22 @@ private: void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); + void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); + void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); + + FileSystemController& fsc; FileSys::VirtualFile romfs; u64 current_process_id = 0; + u32 access_log_program_index = 0; + LogMode log_mode = LogMode::LogToSdCard; + + const Core::Reporter& reporter; }; } // namespace Service::FileSystem diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h new file mode 100644 index 000000000..b3996e275 --- /dev/null +++ b/src/core/hle/service/friend/errors.h @@ -0,0 +1,12 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Friend { + +constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15}; +} diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 5100e376c..42b4ee861 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -2,8 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <queue> #include "common/logging/log.h" +#include "common/uuid.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/friend/errors.h" #include "core/hle/service/friend/friend.h" #include "core/hle/service/friend/interface.h" @@ -17,7 +22,7 @@ public: {0, nullptr, "GetCompletionEvent"}, {1, nullptr, "Cancel"}, {10100, nullptr, "GetFriendListIds"}, - {10101, nullptr, "GetFriendList"}, + {10101, &IFriendService::GetFriendList, "GetFriendList"}, {10102, nullptr, "UpdateFriendInfo"}, {10110, nullptr, "GetFriendProfileImage"}, {10200, nullptr, "SendFriendRequestForApplication"}, @@ -94,6 +99,23 @@ public: } private: + enum class PresenceFilter : u32 { + None = 0, + Online = 1, + OnlinePlay = 2, + OnlineOrOnlinePlay = 3, + }; + + struct SizedFriendFilter { + PresenceFilter presence; + u8 is_favorite; + u8 same_app; + u8 same_app_played; + u8 arbitary_app_played; + u64 group_id; + }; + static_assert(sizeof(SizedFriendFilter) == 0x10, "SizedFriendFilter is an invalid size"); + void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) { // Stub used by Splatoon 2 LOG_WARNING(Service_ACC, "(STUBBED) called"); @@ -107,6 +129,118 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + void GetFriendList(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto friend_offset = rp.Pop<u32>(); + const auto uuid = rp.PopRaw<Common::UUID>(); + [[maybe_unused]] const auto filter = rp.PopRaw<SizedFriendFilter>(); + const auto pid = rp.Pop<u64>(); + LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, + uuid.Format(), pid); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + + rb.Push<u32>(0); // Friend count + // TODO(ogniK): Return a buffer of u64s which are the "NetworkServiceAccountId" + } +}; + +class INotificationService final : public ServiceFramework<INotificationService> { +public: + INotificationService(Common::UUID uuid, Core::System& system) + : ServiceFramework("INotificationService"), uuid(uuid) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &INotificationService::GetEvent, "GetEvent"}, + {1, &INotificationService::Clear, "Clear"}, + {2, &INotificationService::Pop, "Pop"} + }; + // clang-format on + + RegisterHandlers(functions); + + notification_event = Kernel::WritableEvent::CreateEventPair( + system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent"); + } + +private: + void GetEvent(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(notification_event.readable); + } + + void Clear(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + while (!notifications.empty()) { + notifications.pop(); + } + std::memset(&states, 0, sizeof(States)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void Pop(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ACC, "called"); + + if (notifications.empty()) { + LOG_ERROR(Service_ACC, "No notifications in queue!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NO_NOTIFICATIONS); + return; + } + + const auto notification = notifications.front(); + notifications.pop(); + + switch (notification.notification_type) { + case NotificationTypes::HasUpdatedFriendsList: + states.has_updated_friends = false; + break; + case NotificationTypes::HasReceivedFriendRequest: + states.has_received_friend_request = false; + break; + default: + // HOS seems not have an error case for an unknown notification + LOG_WARNING(Service_ACC, "Unknown notification {:08X}", + static_cast<u32>(notification.notification_type)); + break; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<SizedNotificationInfo>(notification); + } + + enum class NotificationTypes : u32 { + HasUpdatedFriendsList = 0x65, + HasReceivedFriendRequest = 0x1 + }; + + struct SizedNotificationInfo { + NotificationTypes notification_type; + INSERT_PADDING_WORDS( + 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now + u64_le account_id; + }; + static_assert(sizeof(SizedNotificationInfo) == 0x10, + "SizedNotificationInfo is an incorrect size"); + + struct States { + bool has_updated_friends; + bool has_received_friend_request; + }; + + Common::UUID uuid; + bool is_event_created = false; + Kernel::EventPair notification_event; + std::queue<SizedNotificationInfo> notifications; + States states{}; }; void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { @@ -116,18 +250,29 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_ACC, "called"); } -Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) {} +void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto uuid = rp.PopRaw<Common::UUID>(); + + LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format()); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<INotificationService>(uuid, system); +} + +Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) + : ServiceFramework(name), module(std::move(module)), system(system) {} Module::Interface::~Interface() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { auto module = std::make_shared<Module>(); - std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); - std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager); - std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager); - std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager); - std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager); + std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager); + std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager); + std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager); + std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager); + std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager); } } // namespace Service::Friend diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h index e762840cb..24f3fc969 100644 --- a/src/core/hle/service/friend/friend.h +++ b/src/core/hle/service/friend/friend.h @@ -6,23 +6,29 @@ #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::Friend { class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(std::shared_ptr<Module> module, const char* name); + explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); ~Interface() override; void CreateFriendService(Kernel::HLERequestContext& ctx); + void CreateNotificationService(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> module; + Core::System& system; }; }; /// Registers all Friend services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::Friend diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp index 5a6840af5..58155f652 100644 --- a/src/core/hle/service/friend/interface.cpp +++ b/src/core/hle/service/friend/interface.cpp @@ -6,11 +6,11 @@ namespace Service::Friend { -Friend::Friend(std::shared_ptr<Module> module, const char* name) - : Interface(std::move(module), name) { +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, nullptr, "CreateNotificationService"}, + {1, &Friend::CreateNotificationService, "CreateNotificationService"}, {2, nullptr, "CreateDaemonSuspendSessionService"}, }; RegisterHandlers(functions); diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h index 1963def39..465a35770 100644 --- a/src/core/hle/service/friend/interface.h +++ b/src/core/hle/service/friend/interface.h @@ -10,7 +10,7 @@ namespace Service::Friend { class Friend final : public Module::Interface { public: - explicit Friend(std::shared_ptr<Module> module, const char* name); + explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name); ~Friend() override; }; diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp new file mode 100644 index 000000000..b591ce31b --- /dev/null +++ b/src/core/hle/service/glue/arp.cpp @@ -0,0 +1,297 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/file_sys/control_metadata.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.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/service.h" + +namespace Service::Glue { + +namespace { +std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) { + const auto& list = system.Kernel().GetProcessList(); + const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) { + return process->GetProcessID() == process_id; + }); + + if (iter == list.end()) { + return std::nullopt; + } + + return (*iter)->GetTitleID(); +} +} // Anonymous namespace + +ARP_R::ARP_R(const Core::System& system, const ARPManager& manager) + : ServiceFramework{"arp:r"}, system(system), manager(manager) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"}, + {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"}, + {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"}, + {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ARP_R::~ARP_R() = default; + +void ARP_R::GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id); + + const auto title_id = GetTitleIDForProcessID(system, process_id); + if (!title_id.has_value()) { + LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_REGISTERED); + return; + } + + const auto res = manager.GetLaunchProperty(*title_id); + + if (res.Failed()) { + LOG_ERROR(Service_ARP, "Failed to get launch property!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(*res); +} + +void ARP_R::GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id); + + const auto res = manager.GetLaunchProperty(title_id); + + if (res.Failed()) { + LOG_ERROR(Service_ARP, "Failed to get launch property!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(*res); +} + +void ARP_R::GetApplicationControlProperty(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id); + + const auto title_id = GetTitleIDForProcessID(system, process_id); + if (!title_id.has_value()) { + LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_REGISTERED); + return; + } + + const auto res = manager.GetControlProperty(*title_id); + + if (res.Failed()) { + LOG_ERROR(Service_ARP, "Failed to get control property!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + ctx.WriteBuffer(*res); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void ARP_R::GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id); + + const auto res = manager.GetControlProperty(title_id); + + if (res.Failed()) { + LOG_ERROR(Service_ARP, "Failed to get control property!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + ctx.WriteBuffer(*res); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +class IRegistrar final : public ServiceFramework<IRegistrar> { + friend class ARP_W; + +public: + explicit IRegistrar( + std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issuer) + : ServiceFramework{"IRegistrar"}, issue_process_id(std::move(issuer)) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IRegistrar::Issue, "Issue"}, + {1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"}, + {2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Issue(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id); + + if (process_id == 0) { + LOG_ERROR(Service_ARP, "Must have non-zero process ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_PROCESS_ID); + return; + } + + if (issued) { + LOG_ERROR(Service_ARP, + "Attempted to issue registrar, but registrar is already issued!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_ACCESS); + return; + } + + issue_process_id(process_id, launch, std::move(control)); + issued = true; + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ARP, "called"); + + if (issued) { + LOG_ERROR( + Service_ARP, + "Attempted to set application launch property, but registrar is already issued!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_ACCESS); + return; + } + + IPC::RequestParser rp{ctx}; + launch = rp.PopRaw<ApplicationLaunchProperty>(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SetApplicationControlProperty(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ARP, "called"); + + if (issued) { + LOG_ERROR( + Service_ARP, + "Attempted to set application control property, but registrar is already issued!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_ACCESS); + return; + } + + control = ctx.ReadBuffer(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issue_process_id; + bool issued = false; + ApplicationLaunchProperty launch; + std::vector<u8> control; +}; + +ARP_W::ARP_W(const Core::System& system, ARPManager& manager) + : ServiceFramework{"arp:w"}, system(system), manager(manager) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"}, + {1, &ARP_W::DeleteProperties, "DeleteProperties"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +ARP_W::~ARP_W() = default; + +void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_ARP, "called"); + + registrar = std::make_shared<IRegistrar>( + [this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) { + const auto res = GetTitleIDForProcessID(system, process_id); + if (!res.has_value()) { + return ERR_NOT_REGISTERED; + } + + return manager.Register(*res, launch, std::move(control)); + }); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(registrar); +} + +void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id); + + if (process_id == 0) { + LOG_ERROR(Service_ARP, "Must have non-zero process ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_INVALID_PROCESS_ID); + return; + } + + const auto title_id = GetTitleIDForProcessID(system, process_id); + + if (!title_id.has_value()) { + LOG_ERROR(Service_ARP, "No title ID for process ID!"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NOT_REGISTERED); + return; + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(manager.Unregister(*title_id)); +} + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h new file mode 100644 index 000000000..d5f8a7e7a --- /dev/null +++ b/src/core/hle/service/glue/arp.h @@ -0,0 +1,43 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Glue { + +class ARPManager; +class IRegistrar; + +class ARP_R final : public ServiceFramework<ARP_R> { +public: + explicit ARP_R(const Core::System& system, const ARPManager& manager); + ~ARP_R() override; + +private: + void GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx); + void GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx); + void GetApplicationControlProperty(Kernel::HLERequestContext& ctx); + void GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx); + + const Core::System& system; + const ARPManager& manager; +}; + +class ARP_W final : public ServiceFramework<ARP_W> { +public: + explicit ARP_W(const Core::System& system, ARPManager& manager); + ~ARP_W() override; + +private: + void AcquireRegistrar(Kernel::HLERequestContext& ctx); + void DeleteProperties(Kernel::HLERequestContext& ctx); + + const Core::System& system; + ARPManager& manager; + std::shared_ptr<IRegistrar> registrar; +}; + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp new file mode 100644 index 000000000..cd89d088f --- /dev/null +++ b/src/core/hle/service/glue/bgtc.cpp @@ -0,0 +1,50 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/glue/bgtc.h" + +namespace Service::Glue { + +BGTC_T::BGTC_T() : ServiceFramework{"bgtc:t"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "NotifyTaskStarting"}, + {2, nullptr, "NotifyTaskFinished"}, + {3, nullptr, "GetTriggerEvent"}, + {4, nullptr, "IsInHalfAwake"}, + {5, nullptr, "NotifyClientName"}, + {6, nullptr, "IsInFullAwake"}, + {11, nullptr, "ScheduleTask"}, + {12, nullptr, "GetScheduledTaskInterval"}, + {13, nullptr, "UnscheduleTask"}, + {14, nullptr, "GetScheduleEvent"}, + {15, nullptr, "SchedulePeriodicTask"}, + {101, nullptr, "GetOperationMode"}, + {102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"}, + {103, nullptr, "WillStayHalfAwakeInsteadSleep"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +BGTC_T::~BGTC_T() = default; + +BGTC_SC::BGTC_SC() : ServiceFramework{"bgtc:sc"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "GetState"}, + {2, nullptr, "GetStateChangedEvent"}, + {3, nullptr, "NotifyEnteringHalfAwake"}, + {4, nullptr, "NotifyLeavingHalfAwake"}, + {5, nullptr, "SetIsUsingSleepUnsupportedDevices"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +BGTC_SC::~BGTC_SC() = default; + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h new file mode 100644 index 000000000..81844f03e --- /dev/null +++ b/src/core/hle/service/glue/bgtc.h @@ -0,0 +1,23 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Glue { + +class BGTC_T final : public ServiceFramework<BGTC_T> { +public: + BGTC_T(); + ~BGTC_T() override; +}; + +class BGTC_SC final : public ServiceFramework<BGTC_SC> { +public: + BGTC_SC(); + ~BGTC_SC() override; +}; + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h new file mode 100644 index 000000000..c2874c585 --- /dev/null +++ b/src/core/hle/service/glue/errors.h @@ -0,0 +1,16 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::Glue { + +constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E}; +constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F}; +constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A}; +constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66}; + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp new file mode 100644 index 000000000..c728e815c --- /dev/null +++ b/src/core/hle/service/glue/glue.cpp @@ -0,0 +1,25 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include "core/core.h" +#include "core/hle/service/glue/arp.h" +#include "core/hle/service/glue/bgtc.h" +#include "core/hle/service/glue/glue.h" + +namespace Service::Glue { + +void InstallInterfaces(Core::System& system) { + // ARP + std::make_shared<ARP_R>(system, system.GetARPManager()) + ->InstallAsService(system.ServiceManager()); + std::make_shared<ARP_W>(system, system.GetARPManager()) + ->InstallAsService(system.ServiceManager()); + + // BackGround Task Controller + std::make_shared<BGTC_T>()->InstallAsService(system.ServiceManager()); + std::make_shared<BGTC_SC>()->InstallAsService(system.ServiceManager()); +} + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h new file mode 100644 index 000000000..112cd238b --- /dev/null +++ b/src/core/hle/service/glue/glue.h @@ -0,0 +1,16 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Core { +class System; +} // namespace Core + +namespace Service::Glue { + +/// Registers all Glue services with the specified service manager. +void InstallInterfaces(Core::System& system); + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/manager.cpp b/src/core/hle/service/glue/manager.cpp new file mode 100644 index 000000000..6da52d2d6 --- /dev/null +++ b/src/core/hle/service/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/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 RESULT_SUCCESS; +} + +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 RESULT_SUCCESS; +} + +void ARPManager::ResetAll() { + entries.clear(); +} + +} // namespace Service::Glue diff --git a/src/core/hle/service/glue/manager.h b/src/core/hle/service/glue/manager.h new file mode 100644 index 000000000..a7f5ce3ee --- /dev/null +++ b/src/core/hle/service/glue/manager.h @@ -0,0 +1,63 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <map> +#include <vector> +#include "common/common_types.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/romfs_factory.h" +#include "core/hle/result.h" + +namespace Service::Glue { + +struct ApplicationLaunchProperty { + u64 title_id; + u32 version; + FileSys::StorageId base_game_storage_id; + FileSys::StorageId update_storage_id; + u8 program_index; + u8 reserved; +}; +static_assert(sizeof(ApplicationLaunchProperty) == 0x10, + "ApplicationLaunchProperty has incorrect size."); + +// A class to manage state related to the arp:w and arp:r services, specifically the registration +// and unregistration of launch and control properties. +class ARPManager { +public: + ARPManager(); + ~ARPManager(); + + // Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was + // previously registered, otherwise ERR_NOT_REGISTERED if it was never registered or + // ERR_INVALID_PROCESS_ID if the title ID is 0. + ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const; + + // Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to + // the provided title ID if it was previously registered, otherwise ERR_NOT_REGISTERED if it was + // never registered or ERR_INVALID_PROCESS_ID if the title ID is 0. + ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const; + + // Adds a new entry to the internal database with the provided parameters, returning + // ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister + // step, and ERR_INVALID_PROCESS_ID if the title ID is 0. + ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control); + + // Removes the registration for the provided title ID from the database, returning + // ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the + // title ID is 0. + ResultCode Unregister(u64 title_id); + + // Removes all entries from the database, always succeeds. Should only be used when resetting + // system state. + void ResetAll(); + +private: + struct MapEntry; + std::map<u64, MapEntry> entries; +}; + +} // namespace Service::Glue diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp index 0993a7815..8091db9d7 100644 --- a/src/core/hle/service/hid/controllers/controller_base.cpp +++ b/src/core/hle/service/hid/controllers/controller_base.cpp @@ -6,7 +6,7 @@ namespace Service::HID { -ControllerBase::ControllerBase() = default; +ControllerBase::ControllerBase(Core::System& system) : system(system) {} ControllerBase::~ControllerBase() = default; void ControllerBase::ActivateController() { diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 5e5097a03..8bc69c372 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h @@ -11,10 +11,14 @@ namespace Core::Timing { class CoreTiming; } +namespace Core { +class System; +} + namespace Service::HID { class ControllerBase { public: - ControllerBase(); + explicit ControllerBase(Core::System& system); virtual ~ControllerBase(); // Called when the controller is initialized @@ -46,5 +50,7 @@ protected: s64_le entry_count; }; static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); + + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index c5c2e032a..8e8263f5b 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp @@ -14,7 +14,8 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; constexpr s32 HID_JOYSTICK_MIN = -0x7fff; enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; -Controller_DebugPad::Controller_DebugPad() = default; +Controller_DebugPad::Controller_DebugPad(Core::System& system) + : ControllerBase(system), system(system) {} Controller_DebugPad::~Controller_DebugPad() = default; void Controller_DebugPad::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h index e584b92ec..6c4de817e 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.h +++ b/src/core/hle/service/hid/controllers/debug_pad.h @@ -16,7 +16,7 @@ namespace Service::HID { class Controller_DebugPad final : public ControllerBase { public: - Controller_DebugPad(); + explicit Controller_DebugPad(Core::System& system); ~Controller_DebugPad() override; // Called when the controller is initialized @@ -89,5 +89,6 @@ private: buttons; std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> analogs; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index a179252e3..80da0a0d3 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -10,7 +10,8 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; -Controller_Gesture::Controller_Gesture() = default; +Controller_Gesture::Controller_Gesture(Core::System& system) + : ControllerBase(system), system(system) {} Controller_Gesture::~Controller_Gesture() = default; void Controller_Gesture::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index f305fe90f..396897527 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -12,7 +12,7 @@ namespace Service::HID { class Controller_Gesture final : public ControllerBase { public: - Controller_Gesture(); + explicit Controller_Gesture(Core::System& system); ~Controller_Gesture() override; // Called when the controller is initialized @@ -59,5 +59,6 @@ private: std::array<GestureState, 17> gesture_states; }; SharedMemory shared_memory{}; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index 92d7bfb52..e587b2e15 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp @@ -12,7 +12,8 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; constexpr u8 KEYS_PER_BYTE = 8; -Controller_Keyboard::Controller_Keyboard() = default; +Controller_Keyboard::Controller_Keyboard(Core::System& system) + : ControllerBase(system), system(system) {} Controller_Keyboard::~Controller_Keyboard() = default; void Controller_Keyboard::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h index 73cd2c7bb..ef586f7eb 100644 --- a/src/core/hle/service/hid/controllers/keyboard.h +++ b/src/core/hle/service/hid/controllers/keyboard.h @@ -15,7 +15,7 @@ namespace Service::HID { class Controller_Keyboard final : public ControllerBase { public: - Controller_Keyboard(); + explicit Controller_Keyboard(Core::System& system); ~Controller_Keyboard() override; // Called when the controller is initialized @@ -53,5 +53,6 @@ private: keyboard_keys; std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> keyboard_mods; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp index 11ab096d9..88f2ca4c1 100644 --- a/src/core/hle/service/hid/controllers/mouse.cpp +++ b/src/core/hle/service/hid/controllers/mouse.cpp @@ -11,7 +11,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; -Controller_Mouse::Controller_Mouse() = default; +Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {} Controller_Mouse::~Controller_Mouse() = default; void Controller_Mouse::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h index 9d46eecbe..df2da6ae3 100644 --- a/src/core/hle/service/hid/controllers/mouse.h +++ b/src/core/hle/service/hid/controllers/mouse.h @@ -14,7 +14,7 @@ namespace Service::HID { class Controller_Mouse final : public ControllerBase { public: - Controller_Mouse(); + explicit Controller_Mouse(Core::System& system); ~Controller_Mouse() override; // Called when the controller is initialized @@ -53,5 +53,6 @@ private: std::unique_ptr<Input::MouseDevice> mouse_device; std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> mouse_button_devices; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index fdd6d79a2..44b668fbf 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -93,7 +93,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { }; } -Controller_NPad::Controller_NPad() = default; +Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} Controller_NPad::~Controller_NPad() = default; void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { @@ -168,9 +168,11 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { } void Controller_NPad::OnInit() { - auto& kernel = Core::System::GetInstance().Kernel(); - styleset_changed_event = Kernel::WritableEvent::CreateEventPair( - kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged"); + auto& kernel = system.Kernel(); + for (std::size_t i = 0; i < styleset_changed_events.size(); i++) { + styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, fmt::format("npad:NpadStyleSetChanged_{}", i)); + } if (!IsControllerActivated()) { return; @@ -453,7 +455,7 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { had_controller_update = true; } if (had_controller_update) { - styleset_changed_event.writable->Signal(); + styleset_changed_events[i].writable->Signal(); } } } @@ -468,7 +470,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const { } void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { - styleset_changed_event.writable->Signal(); hold_type = joy_hold_type; } @@ -479,7 +480,10 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { const std::size_t npad_index = NPadIdToIndex(npad_id); ASSERT(npad_index < shared_memory_entries.size()); - shared_memory_entries[npad_index].pad_assignment = assignment_mode; + if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) { + styleset_changed_events[npad_index].writable->Signal(); + shared_memory_entries[npad_index].pad_assignment = assignment_mode; + } } void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, @@ -498,11 +502,13 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, last_processed_vibration = vibrations.back(); } -Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { +Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent( + u32 npad_id) const { // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should // be signalled at least once, and signaled after a new controller is connected? - styleset_changed_event.writable->Signal(); - return styleset_changed_event.readable; + const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; + styleset_event.writable->Signal(); + return styleset_event.readable; } Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { @@ -548,6 +554,37 @@ void Controller_NPad::DisconnectNPad(u32 npad_id) { connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; } +void Controller_NPad::StartLRAssignmentMode() { + // Nothing internally is used for lr assignment mode. Since we have the ability to set the + // controller types from boot, it doesn't really matter about showing a selection screen + is_in_lr_assignment_mode = true; +} + +void Controller_NPad::StopLRAssignmentMode() { + is_in_lr_assignment_mode = false; +} + +bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { + if (npad_id_1 == NPAD_HANDHELD || npad_id_2 == NPAD_HANDHELD || npad_id_1 == NPAD_UNKNOWN || + npad_id_2 == NPAD_UNKNOWN) { + return true; + } + const auto npad_index_1 = NPadIdToIndex(npad_id_1); + const auto npad_index_2 = NPadIdToIndex(npad_id_2); + + if (!IsControllerSupported(connected_controllers[npad_index_1].type) || + !IsControllerSupported(connected_controllers[npad_index_2].type)) { + return false; + } + + std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); + + InitNewlyAddedControler(npad_index_1); + InitNewlyAddedControler(npad_index_2); + + return true; +} + bool Controller_NPad::IsControllerSupported(NPadControllerType controller) { if (controller == NPadControllerType::Handheld) { // Handheld is not even a supported type, lets stop here @@ -605,10 +642,15 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { return LedPattern{0, 0, 0, 0}; }; } + void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { can_controllers_vibrate = can_vibrate; } +bool Controller_NPad::IsVibrationEnabled() const { + return can_controllers_vibrate; +} + void Controller_NPad::ClearAllConnectedControllers() { for (auto& controller : connected_controllers) { if (controller.is_connected && controller.type != NPadControllerType::None) { @@ -617,6 +659,7 @@ void Controller_NPad::ClearAllConnectedControllers() { } } } + void Controller_NPad::DisconnectAllConnectedControllers() { std::for_each(connected_controllers.begin(), connected_controllers.end(), [](ControllerHolder& controller) { controller.is_connected = false; }); diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 4ff50b3cd..1bc3d55d6 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -20,7 +20,7 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this? class Controller_NPad final : public ControllerBase { public: - Controller_NPad(); + explicit Controller_NPad(Core::System& system); ~Controller_NPad() override; // Called when the controller is initialized @@ -109,7 +109,7 @@ public: void VibrateController(const std::vector<u32>& controller_ids, const std::vector<Vibration>& vibrations); - Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; + Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; Vibration GetLastVibration() const; void AddNewController(NPadControllerType controller); @@ -119,11 +119,16 @@ public: void DisconnectNPad(u32 npad_id); LedPattern GetLedPattern(u32 npad_id); void SetVibrationEnabled(bool can_vibrate); + bool IsVibrationEnabled() const; void ClearAllConnectedControllers(); void DisconnectAllConnectedControllers(); void ConnectAllDisconnectedControllers(); void ClearAllControllers(); + void StartLRAssignmentMode(); + void StopLRAssignmentMode(); + bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); + // Logical OR for all buttons presses on all controllers // Specifically for cheat engine and other features. u32 GetAndResetPressState(); @@ -310,7 +315,8 @@ private: sticks; std::vector<u32> supported_npad_id_types{}; NpadHoldType hold_type{NpadHoldType::Vertical}; - Kernel::EventPair styleset_changed_event; + // Each controller should have their own styleset changed event + std::array<Kernel::EventPair, 10> styleset_changed_events; Vibration last_processed_vibration{}; std::array<ControllerHolder, 10> connected_controllers{}; bool can_controllers_vibrate{true}; @@ -321,5 +327,7 @@ private: void RequestPadStateUpdate(u32 npad_id); std::array<ControllerPad, 10> npad_pad_states{}; bool IsControllerSupported(NPadControllerType controller); + bool is_in_lr_assignment_mode{false}; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp index 946948f5e..9b829341e 100644 --- a/src/core/hle/service/hid/controllers/stubbed.cpp +++ b/src/core/hle/service/hid/controllers/stubbed.cpp @@ -9,7 +9,8 @@ namespace Service::HID { -Controller_Stubbed::Controller_Stubbed() = default; +Controller_Stubbed::Controller_Stubbed(Core::System& system) + : ControllerBase(system), system(system) {} Controller_Stubbed::~Controller_Stubbed() = default; void Controller_Stubbed::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h index 24469f03e..37d7d8538 100644 --- a/src/core/hle/service/hid/controllers/stubbed.h +++ b/src/core/hle/service/hid/controllers/stubbed.h @@ -10,7 +10,7 @@ namespace Service::HID { class Controller_Stubbed final : public ControllerBase { public: - Controller_Stubbed(); + explicit Controller_Stubbed(Core::System& system); ~Controller_Stubbed() override; // Called when the controller is initialized @@ -30,5 +30,6 @@ public: private: bool smart_update{}; std::size_t common_offset{}; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 1a8445a43..25912fd69 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -13,7 +13,8 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; -Controller_Touchscreen::Controller_Touchscreen() = default; +Controller_Touchscreen::Controller_Touchscreen(Core::System& system) + : ControllerBase(system), system(system) {} Controller_Touchscreen::~Controller_Touchscreen() = default; void Controller_Touchscreen::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index 76fc340e9..3429c84db 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h @@ -14,7 +14,7 @@ namespace Service::HID { class Controller_Touchscreen final : public ControllerBase { public: - Controller_Touchscreen(); + explicit Controller_Touchscreen(Core::System& system); ~Controller_Touchscreen() override; // Called when the controller is initialized @@ -69,5 +69,6 @@ private: TouchScreenSharedMemory shared_memory{}; std::unique_ptr<Input::TouchDevice> touch_device; s64_le last_touch{}; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp index 1a9da9576..1bce044b4 100644 --- a/src/core/hle/service/hid/controllers/xpad.cpp +++ b/src/core/hle/service/hid/controllers/xpad.cpp @@ -10,7 +10,7 @@ namespace Service::HID { constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; -Controller_XPad::Controller_XPad() = default; +Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {} Controller_XPad::~Controller_XPad() = default; void Controller_XPad::OnInit() {} diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h index 2864e6617..c445ebec0 100644 --- a/src/core/hle/service/hid/controllers/xpad.h +++ b/src/core/hle/service/hid/controllers/xpad.h @@ -12,7 +12,7 @@ namespace Service::HID { class Controller_XPad final : public ControllerBase { public: - Controller_XPad(); + explicit Controller_XPad(Core::System& system); ~Controller_XPad() override; // Called when the controller is initialized @@ -56,5 +56,6 @@ private: }; static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); SharedMemory shared_memory{}; + Core::System& system; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h new file mode 100644 index 000000000..3583642e7 --- /dev/null +++ b/src/core/hle/service/hid/errors.h @@ -0,0 +1,13 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/result.h" + +namespace Service::HID { + +constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a4ad95d96..8d76ba746 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -16,6 +16,7 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/hid/xcd.h" @@ -41,13 +42,14 @@ constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_C constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; -IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { +IAppletResource::IAppletResource(Core::System& system) + : ServiceFramework("IAppletResource"), system(system) { static const FunctionInfo functions[] = { {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); shared_mem = Kernel::SharedMemory::Create( kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); @@ -73,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); // Register update callbacks - auto& core_timing = Core::System::GetInstance().CoreTiming(); + auto& core_timing = system.CoreTiming(); pad_update_event = core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { UpdateControllers(userdata, cycles_late); @@ -95,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) { } IAppletResource ::~IAppletResource() { - Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); + system.CoreTiming().UnscheduleEvent(pad_update_event, 0); } void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { @@ -107,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { - auto& core_timing = Core::System::GetInstance().CoreTiming(); + auto& core_timing = system.CoreTiming(); const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); for (const auto& controller : controllers) { @@ -140,13 +142,13 @@ private: std::shared_ptr<IAppletResource> Hid::GetAppletResource() { if (applet_resource == nullptr) { - applet_resource = std::make_shared<IAppletResource>(); + applet_resource = std::make_shared<IAppletResource>(system); } return applet_resource; } -Hid::Hid() : ServiceFramework("hid") { +Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &Hid::CreateAppletResource, "CreateAppletResource"}, @@ -202,11 +204,11 @@ Hid::Hid() : ServiceFramework("hid") { {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, - {126, nullptr, "StartLrAssignmentMode"}, - {127, nullptr, "StopLrAssignmentMode"}, + {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"}, + {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, {129, nullptr, "GetNpadHandheldActivationMode"}, - {130, nullptr, "SwapNpadAssignment"}, + {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, @@ -215,8 +217,8 @@ Hid::Hid() : ServiceFramework("hid") { {201, &Hid::SendVibrationValue, "SendVibrationValue"}, {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, - {204, nullptr, "PermitVibration"}, - {205, nullptr, "IsVibrationPermitted"}, + {204, &Hid::PermitVibration, "PermitVibration"}, + {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, {206, &Hid::SendVibrationValues, "SendVibrationValues"}, {207, nullptr, "SendVibrationGcErmCommand"}, {208, nullptr, "GetActualVibrationGcErmCommand"}, @@ -285,7 +287,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>(); + applet_resource = std::make_shared<IAppletResource>(system); } IPC::ResponseBuilder rb{ctx, 2, 0, 1}; @@ -478,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetStyleSetChangedEvent()); + .GetStyleSetChangedEvent(npad_id)); } void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { @@ -678,6 +680,27 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { rb.PushIpcInterface<IActiveVibrationDeviceList>(); } +void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto can_vibrate{rp.Pop<bool>()}; + applet_resource->GetController<Controller_NPad>(HidController::NPad) + .SetVibrationEnabled(can_vibrate); + + LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push( + applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled()); +} + void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto applet_resource_user_id{rp.Pop<u64>()}; @@ -733,6 +756,49 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.StartLRAssignmentMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + controller.StopLRAssignmentMode(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_1{rp.Pop<u32>()}; + const auto npad_2{rp.Pop<u32>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}", + applet_resource_user_id, npad_1, npad_2); + + auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); + IPC::ResponseBuilder rb{ctx, 2}; + if (controller.SwapNpadAssignment(npad_1, npad_2)) { + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_HID, "Npads are not connected!"); + rb.Push(ERR_NPAD_NOT_CONNECTED); + } +} + class HidDbg final : public ServiceFramework<HidDbg> { public: explicit HidDbg() : ServiceFramework{"hid:dbg"} { @@ -988,14 +1054,14 @@ void ReloadInputDevices() { Settings::values.is_device_reload_pending.store(true); } -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<Hid>()->InstallAsService(service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<Hid>(system)->InstallAsService(service_manager); std::make_shared<HidBus>()->InstallAsService(service_manager); std::make_shared<HidDbg>()->InstallAsService(service_manager); std::make_shared<HidSys>()->InstallAsService(service_manager); std::make_shared<HidTmp>()->InstallAsService(service_manager); - std::make_shared<IRS>()->InstallAsService(service_manager); + std::make_shared<IRS>(system)->InstallAsService(service_manager); std::make_shared<IRS_SYS>()->InstallAsService(service_manager); std::make_shared<XCD_SYS>()->InstallAsService(service_manager); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index d3660cad2..35b663679 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -42,7 +42,7 @@ enum class HidController : std::size_t { class IAppletResource final : public ServiceFramework<IAppletResource> { public: - IAppletResource(); + explicit IAppletResource(Core::System& system); ~IAppletResource() override; void ActivateController(HidController controller); @@ -61,7 +61,7 @@ public: private: template <typename T> void MakeController(HidController controller) { - controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(); + controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system); } void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -70,6 +70,7 @@ private: Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; Core::Timing::EventType* pad_update_event; + Core::System& system; std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> controllers{}; @@ -77,7 +78,7 @@ private: class Hid final : public ServiceFramework<Hid> { public: - Hid(); + explicit Hid(Core::System& system); ~Hid() override; std::shared_ptr<IAppletResource> GetAppletResource(); @@ -114,19 +115,25 @@ private: void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx); void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx); + void PermitVibration(Kernel::HLERequestContext& ctx); + void IsVibrationPermitted(Kernel::HLERequestContext& ctx); void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx); void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void StartLrAssignmentMode(Kernel::HLERequestContext& ctx); + void StopLrAssignmentMode(Kernel::HLERequestContext& ctx); + void SwapNpadAssignment(Kernel::HLERequestContext& ctx); std::shared_ptr<IAppletResource> applet_resource; + Core::System& system; }; /// Reload input devices. Used when input configuration changed void ReloadInputDevices(); /// Registers all HID services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 2c4625c99..5e79e2c1a 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -11,7 +11,7 @@ namespace Service::HID { -IRS::IRS() : ServiceFramework{"irs"} { +IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) { // clang-format off static const FunctionInfo functions[] = { {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, @@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} { RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); shared_mem = Kernel::SharedMemory::Create( kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); @@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); - rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks()); + rb.PushRaw<u64>(system.CoreTiming().GetTicks()); rb.PushRaw<u32>(0); } diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 12de6bfb3..eb4e898dd 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -15,7 +15,7 @@ namespace Service::HID { class IRS final : public ServiceFramework<IRS> { public: - explicit IRS(); + explicit IRS(Core::System& system); ~IRS() override; private: @@ -39,6 +39,7 @@ private: void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; const u32 device_handle{0xABCD}; + Core::System& system; }; class IRS_SYS final : public ServiceFramework<IRS_SYS> { diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 5af925515..3164ca26e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -78,7 +78,7 @@ public: class RelocatableObject final : public ServiceFramework<RelocatableObject> { public: - explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { + explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &RelocatableObject::LoadNro, "LoadNro"}, @@ -310,7 +310,7 @@ public: if (!IsValidNROHash(hash)) { LOG_ERROR(Service_LDR, "NRO hash is not present in any currently loaded NRRs (hash={})!", - Common::HexArrayToString(hash)); + Common::HexToString(hash)); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERROR_MISSING_NRR_HASH); return; @@ -345,14 +345,16 @@ public: vm_manager .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) .IsSuccess()); - ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); + ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) + .IsSuccess()); if (bss_size > 0) { ASSERT(vm_manager .MirrorMemory(*map_address + nro_size, bss_address, bss_size, Kernel::MemoryState::ModuleCode) .IsSuccess()); - ASSERT(vm_manager.UnmapRange(bss_address, bss_size).IsSuccess()); + ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) + .IsSuccess()); } vm_manager.ReprotectRange(*map_address, header.text_size, @@ -362,9 +364,10 @@ public: vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, Kernel::VMAPermission::ReadWrite); - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + system.InvalidateCpuInstructionCaches(); - nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size}); + nro.insert_or_assign(*map_address, + NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -409,11 +412,25 @@ public: } auto& vm_manager = Core::CurrentProcess()->VMManager(); - const auto& nro_size = iter->second.size; + const auto& nro_info = iter->second; - ASSERT(vm_manager.UnmapRange(nro_address, nro_size).IsSuccess()); + // Unmap the mirrored memory + ASSERT( + vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess()); + + // Reprotect the source memory + ASSERT(vm_manager + .ReprotectRange(nro_info.nro_address, nro_info.nro_size, + Kernel::VMAPermission::ReadWrite) + .IsSuccess()); + if (nro_info.bss_size > 0) { + ASSERT(vm_manager + .ReprotectRange(nro_info.bss_address, nro_info.bss_size, + Kernel::VMAPermission::ReadWrite) + .IsSuccess()); + } - Core::System::GetInstance().InvalidateCpuInstructionCaches(); + system.InvalidateCpuInstructionCaches(); nro.erase(iter); IPC::ResponseBuilder rb{ctx, 2}; @@ -473,7 +490,10 @@ private: struct NROInfo { SHA256Hash hash; - u64 size; + VAddr nro_address; + u64 nro_size; + VAddr bss_address; + u64 bss_size; }; bool initialized = false; @@ -496,13 +516,14 @@ private: Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && Common::Is4KBAligned(header.rw_size); } + Core::System& system; }; -void InstallInterfaces(SM::ServiceManager& sm) { +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<DebugMonitor>()->InstallAsService(sm); std::make_shared<ProcessManager>()->InstallAsService(sm); std::make_shared<Shell>()->InstallAsService(sm); - std::make_shared<RelocatableObject>()->InstallAsService(sm); + std::make_shared<RelocatableObject>(system)->InstallAsService(sm); } } // namespace Service::LDR diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h index 412410c4f..7ac8c0b65 100644 --- a/src/core/hle/service/ldr/ldr.h +++ b/src/core/hle/service/ldr/ldr.h @@ -11,6 +11,6 @@ class ServiceManager; namespace Service::LDR { /// Registers all LDR services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& sm); +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::LDR diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index ce84e25ed..0b3923ad9 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -48,7 +48,7 @@ public: {19, nullptr, "Export"}, {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, {21, &IDatabaseService::GetIndex, "GetIndex"}, - {22, nullptr, "SetInterfaceVersion"}, + {22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"}, {23, nullptr, "Convert"}, }; // clang-format on @@ -350,8 +350,22 @@ private: rb.Push(index); } + void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + current_interface_version = rp.PopRaw<u32>(); + + LOG_DEBUG(Service_Mii, "called, interface_version={:08X}", current_interface_version); + + UNIMPLEMENTED_IF(current_interface_version != 1); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + MiiManager db; + u32 current_interface_version = 0; + // Last read offsets of Get functions std::array<u32, 4> offsets{}; }; diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp index 131b01d62..8d0353075 100644 --- a/src/core/hle/service/mii/mii_manager.cpp +++ b/src/core/hle/service/mii/mii_manager.cpp @@ -175,6 +175,10 @@ MiiStoreData ConvertInfoToStoreData(const MiiInfo& info) { } // namespace std::ostream& operator<<(std::ostream& os, Source source) { + if (static_cast<std::size_t>(source) >= SOURCE_NAMES.size()) { + return os << "[UNKNOWN SOURCE]"; + } + os << SOURCE_NAMES.at(static_cast<std::size_t>(source)); return os; } diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index a5cb06f8a..a42c22d44 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -23,9 +23,9 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP, constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); } // namespace ErrCodes -Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) - : ServiceFramework(name), module(std::move(module)) { - auto& kernel = Core::System::GetInstance().Kernel(); +Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name) + : ServiceFramework(name), module(std::move(module)), system(system) { + auto& kernel = system.Kernel(); nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, "IUser:NFCTagDetected"); } @@ -34,8 +34,8 @@ Module::Interface::~Interface() = default; class IUser final : public ServiceFramework<IUser> { public: - IUser(Module::Interface& nfp_interface) - : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { + IUser(Module::Interface& nfp_interface, Core::System& system) + : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) { static const FunctionInfo functions[] = { {0, &IUser::Initialize, "Initialize"}, {1, &IUser::Finalize, "Finalize"}, @@ -65,7 +65,7 @@ public: }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); deactivate_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); availability_change_event = Kernel::WritableEvent::CreateEventPair( @@ -324,6 +324,7 @@ private: Kernel::EventPair deactivate_event; Kernel::EventPair availability_change_event; const Module::Interface& nfp_interface; + Core::System& system; }; void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { @@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IUser>(*this); + rb.PushIpcInterface<IUser>(*this, system); } bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { @@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const return amiibo; } -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { auto module = std::make_shared<Module>(); - std::make_shared<NFP_User>(module)->InstallAsService(service_manager); + std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager); } } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h index a1817e991..9718ef745 100644 --- a/src/core/hle/service/nfp/nfp.h +++ b/src/core/hle/service/nfp/nfp.h @@ -16,7 +16,7 @@ class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(std::shared_ptr<Module> module, const char* name); + explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name); ~Interface() override; struct ModelInfo { @@ -43,9 +43,10 @@ public: protected: std::shared_ptr<Module> module; + Core::System& system; }; }; -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::NFP diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp index 784a87c1b..298184f17 100644 --- a/src/core/hle/service/nfp/nfp_user.cpp +++ b/src/core/hle/service/nfp/nfp_user.cpp @@ -6,8 +6,8 @@ namespace Service::NFP { -NFP_User::NFP_User(std::shared_ptr<Module> module) - : Module::Interface(std::move(module), "nfp:user") { +NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system) + : Module::Interface(std::move(module), system, "nfp:user") { static const FunctionInfo functions[] = { {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, }; diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h index 65d9aaf48..1686ebf20 100644 --- a/src/core/hle/service/nfp/nfp_user.h +++ b/src/core/hle/service/nfp/nfp_user.h @@ -10,7 +10,7 @@ namespace Service::NFP { class NFP_User final : public Module::Interface { public: - explicit NFP_User(std::shared_ptr<Module> module); + explicit NFP_User(std::shared_ptr<Module> module, Core::System& system); ~NFP_User() override; }; diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 76b12b482..24d1813a7 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -31,7 +31,7 @@ public: class IRequest final : public ServiceFramework<IRequest> { public: - explicit IRequest() : ServiceFramework("IRequest") { + explicit IRequest(Core::System& system) : ServiceFramework("IRequest") { static const FunctionInfo functions[] = { {0, &IRequest::GetRequestState, "GetRequestState"}, {1, &IRequest::GetResult, "GetResult"}, @@ -61,7 +61,7 @@ public: }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, "IRequest:Event1"); event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, @@ -130,7 +130,7 @@ public: class IGeneralService final : public ServiceFramework<IGeneralService> { public: - IGeneralService(); + IGeneralService(Core::System& system); private: void GetClientId(Kernel::HLERequestContext& ctx) { @@ -155,7 +155,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IRequest>(); + rb.PushIpcInterface<IRequest>(system); } void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -198,9 +198,11 @@ private: rb.Push(RESULT_SUCCESS); rb.Push<u8>(0); } + Core::System& system; }; -IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { +IGeneralService::IGeneralService(Core::System& system) + : ServiceFramework("IGeneralService"), system(system) { static const FunctionInfo functions[] = { {1, &IGeneralService::GetClientId, "GetClientId"}, {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, @@ -245,7 +247,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { class NetworkInterface final : public ServiceFramework<NetworkInterface> { public: - explicit NetworkInterface(const char* name) : ServiceFramework{name} { + explicit NetworkInterface(const char* name, Core::System& system) + : ServiceFramework{name}, system(system) { static const FunctionInfo functions[] = { {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"}, @@ -258,7 +261,7 @@ public: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); + rb.PushIpcInterface<IGeneralService>(system); } void CreateGeneralService(Kernel::HLERequestContext& ctx) { @@ -266,14 +269,17 @@ public: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IGeneralService>(); + rb.PushIpcInterface<IGeneralService>(system); } + +private: + Core::System& system; }; -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager); - std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager); - std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager); + std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager); + std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager); } } // namespace Service::NIFM diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 4616b3b48..6857e18f9 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -8,9 +8,13 @@ namespace Service::SM { class ServiceManager; } +namespace Core { +class System; +} + namespace Service::NIFM { /// Registers all NIFM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::NIFM diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index f319a3ca1..75d414952 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -126,7 +126,7 @@ public: class IEnsureNetworkClockAvailabilityService final : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { public: - IEnsureNetworkClockAvailabilityService() + explicit IEnsureNetworkClockAvailabilityService(Core::System& system) : ServiceFramework("IEnsureNetworkClockAvailabilityService") { static const FunctionInfo functions[] = { {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, @@ -139,7 +139,7 @@ public: }; RegisterHandlers(functions); - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); finished_event = Kernel::WritableEvent::CreateEventPair( kernel, Kernel::ResetType::Automatic, "IEnsureNetworkClockAvailabilityService:FinishEvent"); @@ -200,7 +200,7 @@ private: class NTC final : public ServiceFramework<NTC> { public: - explicit NTC() : ServiceFramework{"ntc"} { + explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) { // clang-format off static const FunctionInfo functions[] = { {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, @@ -218,7 +218,7 @@ private: IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); + rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system); } // TODO(ogniK): Do we need these? @@ -235,13 +235,14 @@ private: IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + Core::System& system; }; -void InstallInterfaces(SM::ServiceManager& sm) { +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) { std::make_shared<NIM>()->InstallAsService(sm); std::make_shared<NIM_ECA>()->InstallAsService(sm); std::make_shared<NIM_SHP>()->InstallAsService(sm); - std::make_shared<NTC>()->InstallAsService(sm); + std::make_shared<NTC>(system)->InstallAsService(sm); } } // namespace Service::NIM diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index 2a2a92df0..dbe25dc01 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -8,8 +8,12 @@ namespace Service::SM { class ServiceManager; } +namespace Core { +class System; +} + namespace Service::NIM { -void InstallInterfaces(SM::ServiceManager& sm); +void InstallInterfaces(SM::ServiceManager& sm, Core::System& system); } // namespace Service::NIM diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index ce88a2941..15c156ce1 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -617,7 +617,8 @@ public: } }; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); @@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { std::make_shared<NS_SU>()->InstallAsService(service_manager); std::make_shared<NS_VM>()->InstallAsService(service_manager); - std::make_shared<PL_U>()->InstallAsService(service_manager); + std::make_shared<PL_U>(system)->InstallAsService(service_manager); } } // namespace Service::NS diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h index 0e8256cb4..13a64ad88 100644 --- a/src/core/hle/service/ns/ns.h +++ b/src/core/hle/service/ns/ns.h @@ -6,7 +6,13 @@ #include "core/hle/service/service.h" -namespace Service::NS { +namespace Service { + +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + +namespace NS { class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { public: @@ -91,6 +97,7 @@ private: }; /// Registers all NS services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); -} // namespace Service::NS +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index ad176f89d..7dcdb4a07 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -77,7 +77,7 @@ enum class LoadState : u32 { Done = 1, }; -static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, +static void DecryptSharedFont(const std::vector<u32>& input, Kernel::PhysicalMemory& output, std::size_t& offset) { ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); @@ -94,7 +94,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou offset += transformed_font.size() * sizeof(u32); } -static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, +static void EncryptSharedFont(const std::vector<u8>& input, Kernel::PhysicalMemory& output, std::size_t& offset) { ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; @@ -121,7 +121,7 @@ struct PL_U::Impl { return shared_font_regions.at(index); } - void BuildSharedFontsRawRegions(const std::vector<u8>& input) { + void BuildSharedFontsRawRegions(const Kernel::PhysicalMemory& input) { // As we can derive the xor key we can just populate the offsets // based on the shared memory dump unsigned cur_offset = 0; @@ -144,13 +144,15 @@ struct PL_U::Impl { Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; /// Backing memory for the shared font data - std::shared_ptr<std::vector<u8>> shared_font; + std::shared_ptr<Kernel::PhysicalMemory> shared_font; // Automatically populated based on shared_fonts dump or system archives. std::vector<FontRegion> shared_font_regions; }; -PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { +PL_U::PL_U(Core::System& system) + : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) { + static const FunctionInfo functions[] = { {0, &PL_U::RequestLoad, "RequestLoad"}, {1, &PL_U::GetLoadState, "GetLoadState"}, @@ -160,13 +162,16 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, }; RegisterHandlers(functions); + + auto& fsc = system.GetFileSystemController(); + // Attempt to load shared font data from disk - const auto* nand = FileSystem::GetSystemNANDContents(); + const auto* nand = fsc.GetSystemNANDContents(); std::size_t offset = 0; // Rebuild shared fonts from data ncas if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), FileSys::ContentRecordType::Data)) { - impl->shared_font = std::make_shared<std::vector<u8>>(SHARED_FONT_MEM_SIZE); + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>(SHARED_FONT_MEM_SIZE); for (auto font : SHARED_FONTS) { const auto nca = nand->GetEntry(static_cast<u64>(font.first), FileSys::ContentRecordType::Data); @@ -207,7 +212,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { } } else { - impl->shared_font = std::make_shared<std::vector<u8>>( + impl->shared_font = std::make_shared<Kernel::PhysicalMemory>( SHARED_FONT_MEM_SIZE); // Shared memory needs to always be allocated and a fixed size const std::string user_path = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir); @@ -324,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { Kernel::MemoryState::Shared); // Create shared font memory object - auto& kernel = Core::System::GetInstance().Kernel(); + auto& kernel = system.Kernel(); impl->shared_font_mem = Kernel::SharedMemory::Create( kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h index 253f26a2a..1063f4204 100644 --- a/src/core/hle/service/ns/pl_u.h +++ b/src/core/hle/service/ns/pl_u.h @@ -7,11 +7,17 @@ #include <memory> #include "core/hle/service/service.h" -namespace Service::NS { +namespace Service { + +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + +namespace NS { class PL_U final : public ServiceFramework<PL_U> { public: - PL_U(); + explicit PL_U(Core::System& system); ~PL_U() override; private: @@ -24,6 +30,9 @@ private: struct Impl; std::unique_ptr<Impl> impl; + Core::System& system; }; -} // namespace Service::NS +} // namespace NS + +} // namespace Service diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 4f6042b00..1b52511a5 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -8,6 +8,12 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} namespace Service::Nvidia::Devices { @@ -15,7 +21,7 @@ namespace Service::Nvidia::Devices { /// implement the ioctl interface. class nvdevice { public: - nvdevice() = default; + explicit nvdevice(Core::System& system) : system{system} {}; virtual ~nvdevice() = default; union Ioctl { u32_le raw; @@ -33,7 +39,12 @@ public: * @param output A buffer where the output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; + virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) = 0; + +protected: + Core::System& system; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 20c7c39aa..f764388bc 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -13,10 +13,13 @@ namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvdisp_disp0 ::~nvdisp_disp0() = default; -u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } @@ -34,9 +37,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform, crop_rect}; - auto& instance = Core::System::GetInstance(); - instance.GetPerfStats().EndGameFrame(); - instance.GPU().SwapBuffers(framebuffer); + system.GetPerfStats().EndGameFrame(); + system.GPU().SwapBuffers(&framebuffer); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 12f3ef825..6fcdeee84 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -17,10 +17,12 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); + explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvdisp_disp0() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index af62d33d2..6bc053f27 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -22,10 +22,13 @@ enum { }; } -nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_as_gpu::~nvhost_as_gpu() = default; -u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -65,7 +68,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; if (params.flags & 1) { params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); @@ -85,7 +88,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); for (const auto& entry : entries) { LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", entry.offset, entry.nvmap_handle, entry.pages); @@ -136,7 +139,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou // case to prevent unexpected behavior. ASSERT(object->id == params.nvmap_handle); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); if (params.flags & 1) { params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); @@ -173,8 +176,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou return 0; } - params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset, - itr->second.size); + params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); std::memcpy(output.data(), ¶ms, output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index eb14b1da8..169fb8f0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -17,10 +17,12 @@ class nvmap; class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_as_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b39fb9ef9..ff6b1abae 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -7,14 +7,21 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" +#include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvhost_ctrl::nvhost_ctrl() = default; +nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) + : nvdevice(system), events_interface{events_interface} {} nvhost_ctrl::~nvhost_ctrl() = default; -u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -22,11 +29,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< case IoctlCommand::IocGetConfigCommand: return NvOsGetConfigU32(input, output); case IoctlCommand::IocCtrlEventWaitCommand: - return IocCtrlEventWait(input, output, false); + return IocCtrlEventWait(input, output, false, ctrl); case IoctlCommand::IocCtrlEventWaitAsyncCommand: - return IocCtrlEventWait(input, output, true); + return IocCtrlEventWait(input, output, true, ctrl); case IoctlCommand::IocCtrlEventRegisterCommand: return IocCtrlEventRegister(input, output); + case IoctlCommand::IocCtrlEventUnregisterCommand: + return IocCtrlEventUnregister(input, output); + case IoctlCommand::IocCtrlEventSignalCommand: + return IocCtrlEventSignal(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -41,23 +52,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async) { + bool is_async, IoctlCtrl& ctrl) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, - "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", + params.syncpt_id, params.threshold, params.timeout, is_async); - // TODO(Subv): Implement actual syncpt waiting. - params.value = 0; + if (params.syncpt_id >= MaxSyncPoints) { + return NvResult::BadParameter; + } + + auto& gpu = system.GPU(); + // This is mostly to take into account unimplemented features. As synced + // gpu is always synced. + if (!gpu.IsAsync()) { + return NvResult::Success; + } + auto lock = gpu.LockSync(); + const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); + const s32 diff = current_syncpoint_value - params.threshold; + if (diff >= 0) { + params.value = current_syncpoint_value; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Success; + } + const u32 target_value = current_syncpoint_value - diff; + + if (!is_async) { + params.value = 0; + } + + if (params.timeout == 0) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } + + u32 event_id; + if (is_async) { + event_id = params.value & 0x00FF; + if (event_id >= MaxNvEvents) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::BadParameter; + } + } else { + if (ctrl.fresh_call) { + const auto result = events_interface.GetFreeEvent(); + if (result) { + event_id = *result; + } else { + LOG_CRITICAL(Service_NVDRV, "No Free Events available!"); + event_id = params.value & 0x00FF; + } + } else { + event_id = ctrl.event_id; + } + } + + EventState status = events_interface.status[event_id]; + if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) { + events_interface.SetEventStatus(event_id, EventState::Waiting); + events_interface.assigned_syncpt[event_id] = params.syncpt_id; + events_interface.assigned_value[event_id] = target_value; + if (is_async) { + params.value = params.syncpt_id << 4; + } else { + params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + } + params.value |= event_id; + events_interface.events[event_id].writable->Clear(); + gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); + if (!is_async && ctrl.fresh_call) { + ctrl.must_delay = true; + ctrl.timeout = params.timeout; + ctrl.event_id = event_id; + return NvResult::Timeout; + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::BadParameter; } u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - // TODO(bunnei): Implement this. - return 0; + IocCtrlEventRegisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.RegisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventUnregisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (!events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.UnregisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventSignalParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization + // It is believed from RE to cancel the GPU Event. However, better research is required + u32 event_id = params.user_event_id & 0x00FF; + LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.status[event_id] == EventState::Waiting) { + auto& gpu = system.GPU(); + if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], + events_interface.assigned_value[event_id])) { + events_interface.LiberateEvent(event_id); + events_interface.events[event_id].writable->Signal(); + } + } + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 6d0de2212..9898623de 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -8,15 +8,18 @@ #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: - nvhost_ctrl(); + explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); ~nvhost_ctrl() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { @@ -132,9 +135,16 @@ private: u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, + IoctlCtrl& ctrl); u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output); + + EventInterface& events_interface; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0e28755bd..389ace76f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -12,16 +12,18 @@ namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; -u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, + const std::vector<u8>& input2, std::vector<u8>& output, + std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::IocGetCharacteristicsCommand: - return GetCharacteristics(input, output); + return GetCharacteristics(input, output, output2, version); case IoctlCommand::IocGetTPCMasksCommand: return GetTPCMasks(input, output); case IoctlCommand::IocGetActiveSlotMaskCommand: @@ -43,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec return 0; } -u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& output2, IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called"); IoctlCharacteristics params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -84,7 +87,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto params.gc.gr_compbit_store_base_hw = 0x0; params.gpu_characteristics_buf_size = 0xA0; params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) - std::memcpy(output.data(), ¶ms, output.size()); + + if (version == IoctlVersion::Version3) { + std::memcpy(output.data(), input.data(), output.size()); + std::memcpy(output2.data(), ¶ms.gc, output2.size()); + } else { + std::memcpy(output.data(), ¶ms, output.size()); + } return 0; } @@ -185,7 +194,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); + const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); params.gpu_time = static_cast<u64_le>(ns.count()); std::memcpy(output.data(), ¶ms, output.size()); return 0; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 240435eea..642b0a2cb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: - nvhost_ctrl_gpu(); + explicit nvhost_ctrl_gpu(Core::System& system); ~nvhost_ctrl_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { @@ -161,7 +163,8 @@ private: }; static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); - u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); + u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output, + std::vector<u8>& output2, IoctlVersion version); u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 8ce7bc7a5..2b8d1bef6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -13,10 +13,13 @@ namespace Service::Nvidia::Devices { -nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_gpu::~nvhost_gpu() = default; -u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -48,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u return SubmitGPFIFO(input, output); } if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { - return KickoffPB(input, output); + return KickoffPB(input, output, input2, version); } } @@ -119,8 +122,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, params.unk3); - params.fence_out.id = 0; - params.fence_out.value = 0; + auto& gpu = system.GPU(); + params.fence_out.id = assigned_syncpoints; + params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); + assigned_syncpoints++; std::memcpy(output.data(), ¶ms, output.size()); return 0; } @@ -142,8 +147,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp } IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, + params.num_entries, params.flags.raw); ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(Tegra::CommandListHeader), @@ -153,31 +158,52 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); + UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); + + auto& gpu = system.GPU(); + u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); + if (params.flags.increment.Value()) { + params.fence_out.value += current_syncpoint_value; + } else { + params.fence_out.value = current_syncpoint_value; + } + gpu.PushGPUEntries(std::move(entries)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return 0; } -u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, + const std::vector<u8>& input2, IoctlVersion version) { if (input.size() < sizeof(IoctlSubmitGpfifo)) { UNIMPLEMENTED(); } IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); - LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, + params.num_entries, params.flags.raw); Tegra::CommandList entries(params.num_entries); - Memory::ReadBlock(params.address, entries.data(), - params.num_entries * sizeof(Tegra::CommandListHeader)); - - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + if (version == IoctlVersion::Version2) { + std::memcpy(entries.data(), input2.data(), + params.num_entries * sizeof(Tegra::CommandListHeader)); + } else { + Memory::ReadBlock(params.address, entries.data(), + params.num_entries * sizeof(Tegra::CommandListHeader)); + } + UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); + UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); + + auto& gpu = system.GPU(); + u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); + if (params.flags.increment.Value()) { + params.fence_out.value += current_syncpoint_value; + } else { + params.fence_out.value = current_syncpoint_value; + } + gpu.PushGPUEntries(std::move(entries)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 62beb5c0c..d056dd046 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdata.h" namespace Service::Nvidia::Devices { @@ -20,10 +21,12 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { @@ -113,11 +116,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - struct IoctlFence { - u32_le id; - u32_le value; - }; - static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); + static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries; @@ -132,13 +131,13 @@ private: static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); struct IoctlAllocGpfifoEx2 { - u32_le num_entries; // in - u32_le flags; // in - u32_le unk0; // in (1 works) - IoctlFence fence_out; // out - u32_le unk1; // in - u32_le unk2; // in - u32_le unk3; // in + u32_le num_entries; // in + u32_le flags; // in + u32_le unk0; // in (1 works) + Fence fence_out; // out + u32_le unk1; // in + u32_le unk2; // in + u32_le unk3; // in }; static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); @@ -153,10 +152,16 @@ private: struct IoctlSubmitGpfifo { u64_le address; // pointer to gpfifo entry structs u32_le num_entries; // number of fence objects being submitted - u32_le flags; - IoctlFence fence_out; // returned new fence object for others to wait on - }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), + union { + u32_le raw; + BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list + BitField<1, 1, u32_le> add_increment; // append an increment to the list + BitField<2, 1, u32_le> new_hw_format; // Mostly ignored + BitField<8, 1, u32_le> increment; // increment the returned fence + } flags; + Fence fence_out; // returned new fence object for others to wait on + }; + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -179,11 +184,13 @@ private: u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); - u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output); + u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, + const std::vector<u8>& input2, IoctlVersion version); u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); std::shared_ptr<nvmap> nvmap_dev; + u32 assigned_syncpoints{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f5e8ea7c3..bdae8b887 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,10 +10,12 @@ namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec() = default; +nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} nvhost_nvdec::~nvhost_nvdec() = default; -u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0e7b284f8..cbdac8069 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvdevice { public: - nvhost_nvdec(); + explicit nvhost_nvdec(Core::System& system); ~nvhost_nvdec() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 3e0951ab0..96e7b7dab 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -10,10 +10,12 @@ namespace Service::Nvidia::Devices { -nvhost_nvjpg::nvhost_nvjpg() = default; +nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} nvhost_nvjpg::~nvhost_nvjpg() = default; -u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 89fd5e95e..98dcac52f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { class nvhost_nvjpg final : public nvdevice { public: - nvhost_nvjpg(); + explicit nvhost_nvjpg(Core::System& system); ~nvhost_nvjpg() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index d544f0f31..c695b8863 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -10,10 +10,12 @@ namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic() = default; +nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} nvhost_vic::~nvhost_vic() = default; -u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index fc24c3f9c..bec32bea1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvdevice { public: - nvhost_vic(); + explicit nvhost_vic(Core::System& system); ~nvhost_vic() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 1ec796fc6..8c742316c 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -18,7 +18,7 @@ enum { }; } -nvmap::nvmap() = default; +nvmap::nvmap(Core::System& system) : nvdevice(system) {} nvmap::~nvmap() = default; VAddr nvmap::GetObjectAddress(u32 handle) const { @@ -28,7 +28,9 @@ VAddr nvmap::GetObjectAddress(u32 handle) const { return object->addr; } -u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::Create: return IocCreate(input, output); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 396230c19..73c2e8809 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -16,13 +16,15 @@ namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - nvmap(); + explicit nvmap(Core::System& system); ~nvmap() override; /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) override; /// Represents an nvmap object. struct Object { diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index b60fc748b..5e0c23602 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -8,12 +8,18 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.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"); @@ -27,20 +33,75 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) { rb.Push<u32>(0); } -void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_NVDRV, "called"); - +void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); u32 command = rp.Pop<u32>(); - std::vector<u8> output(ctx.GetWriteBufferSize()); - + /// Ioctl 3 has 2 outputs, first in the input params, second is the result + std::vector<u8> output(ctx.GetWriteBufferSize(0)); + std::vector<u8> output2; + if (version == IoctlVersion::Version3) { + output2.resize((ctx.GetWriteBufferSize(1))); + } + + /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer. + /// KickOfPB uses this + auto input = ctx.ReadBuffer(0); + + std::vector<u8> input2; + if (version == IoctlVersion::Version2) { + input2 = ctx.ReadBuffer(1); + } + + IoctlCtrl ctrl{}; + + u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version); + + if (ctrl.must_delay) { + ctrl.fresh_call = false; + ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, + [=](Kernel::SharedPtr<Kernel::Thread> thread, + Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + IoctlCtrl ctrl2{ctrl}; + std::vector<u8> tmp_output = output; + std::vector<u8> tmp_output2 = output2; + u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, + tmp_output2, ctrl2, version); + ctx.WriteBuffer(tmp_output, 0); + if (version == IoctlVersion::Version3) { + ctx.WriteBuffer(tmp_output2, 1); + } + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(result); + }, + nvdrv->GetEventWriteable(ctrl.event_id)); + } else { + ctx.WriteBuffer(output); + if (version == IoctlVersion::Version3) { + ctx.WriteBuffer(output2, 1); + } + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); + rb.Push(result); +} + +void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlBase(ctx, IoctlVersion::Version1); +} - ctx.WriteBuffer(output); +void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlBase(ctx, IoctlVersion::Version2); +} + +void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_NVDRV, "called"); + IoctlBase(ctx, IoctlVersion::Version3); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { @@ -66,13 +127,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); - u32 event_id = rp.Pop<u32>(); + // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 + u32 event_id = rp.Pop<u32>() & 0x000000FF; LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(query_event.readable); - rb.Push<u32>(0); + if (event_id < MaxNvEvents) { + rb.PushCopyObjects(nvdrv->GetEvent(event_id)); + rb.Push<u32>(NvResult::Success); + } else { + rb.Push<u32>(0); + rb.Push<u32>(NvResult::BadParameter); + } } void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { @@ -122,15 +189,11 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) {8, &NVDRV::SetClientPID, "SetClientPID"}, {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, {10, nullptr, "InitializeDevtools"}, - {11, &NVDRV::Ioctl, "Ioctl2"}, - {12, nullptr, "Ioctl3"}, + {11, &NVDRV::Ioctl2, "Ioctl2"}, + {12, &NVDRV::Ioctl3, "Ioctl3"}, {13, &NVDRV::FinishInitialize, "FinishInitialize"}, }; RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, - "NVDRV::query_event"); } NVDRV::~NVDRV() = default; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 5b4889910..9269ce00c 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -19,9 +19,13 @@ public: NVDRV(std::shared_ptr<Module> nvdrv, const char* name); ~NVDRV() override; + void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value); + private: void Open(Kernel::HLERequestContext& ctx); void Ioctl(Kernel::HLERequestContext& ctx); + void Ioctl2(Kernel::HLERequestContext& ctx); + void Ioctl3(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); void Initialize(Kernel::HLERequestContext& ctx); void QueryEvent(Kernel::HLERequestContext& ctx); @@ -29,12 +33,11 @@ private: void FinishInitialize(Kernel::HLERequestContext& ctx); void GetStatus(Kernel::HLERequestContext& ctx); void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); + void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version); std::shared_ptr<Module> nvdrv; u64 pid{}; - - Kernel::EventPair query_event; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h new file mode 100644 index 000000000..529b03471 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -0,0 +1,54 @@ +#pragma once + +#include <array> +#include "common/common_types.h" + +namespace Service::Nvidia { + +constexpr u32 MaxSyncPoints = 192; +constexpr u32 MaxNvEvents = 64; + +struct Fence { + s32 id; + u32 value; +}; + +static_assert(sizeof(Fence) == 8, "Fence has wrong size"); + +struct MultiFence { + u32 num_fences; + std::array<Fence, 4> fences; +}; + +enum NvResult : u32 { + Success = 0, + BadParameter = 4, + Timeout = 5, + ResourceError = 15, +}; + +enum class EventState { + Free = 0, + Registered = 1, + Waiting = 2, + Busy = 3, +}; + +enum class IoctlVersion : u32 { + Version1, + Version2, + Version3, +}; + +struct IoctlCtrl { + // First call done to the servioce for services that call itself again after a call. + bool fresh_call{true}; + // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep + bool must_delay{}; + // Timeout for the delay + s64 timeout{}; + // NV Event Id + s32 event_id{-1}; +}; + +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 6e4b8f2c6..307a7e928 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -4,7 +4,10 @@ #include <utility> +#include <fmt/format.h> #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -22,8 +25,9 @@ namespace Service::Nvidia { -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) { - auto module_ = std::make_shared<Module>(); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system) { + auto module_ = std::make_shared<Module>(system); std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); @@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger nvflinger.SetNVDrvInstance(module_); } -Module::Module() { - auto nvmap_dev = std::make_shared<Devices::nvmap>(); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); - devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); +Module::Module(Core::System& system) { + auto& kernel = system.Kernel(); + for (u32 i = 0; i < MaxNvEvents; i++) { + std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); + events_interface.events[i] = Kernel::WritableEvent::CreateEventPair( + kernel, Kernel::ResetType::Automatic, event_label); + events_interface.status[i] = EventState::Free; + events_interface.registered[i] = false; + } + auto nvmap_dev = std::make_shared<Devices::nvmap>(system); + devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); + devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev); + devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); devices["/dev/nvmap"] = nvmap_dev; - devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev); - devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); - devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); - devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(); - devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); + devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); + devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface); + devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system); + devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); + devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system); } Module::~Module() = default; @@ -59,12 +71,14 @@ u32 Module::Open(const std::string& device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); auto& device = itr->second; - return device->ioctl({command}, input, output); + return device->ioctl({command}, input, input2, output, output2, ctrl, version); } ResultCode Module::Close(u32 fd) { @@ -77,4 +91,22 @@ ResultCode Module::Close(u32 fd) { return RESULT_SUCCESS; } +void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { + for (u32 i = 0; i < MaxNvEvents; i++) { + if (events_interface.assigned_syncpt[i] == syncpoint_id && + events_interface.assigned_value[i] == value) { + events_interface.LiberateEvent(i); + events_interface.events[i].writable->Signal(); + } + } +} + +Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { + return events_interface.events[event_id].readable; +} + +Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { + return events_interface.events[event_id].writable; +} + } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 53564f696..f8bb28969 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -8,8 +8,14 @@ #include <unordered_map> #include <vector> #include "common/common_types.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::NVFlinger { class NVFlinger; } @@ -20,16 +26,72 @@ namespace Devices { class nvdevice; } -struct IoctlFence { - u32 id; - u32 value; +struct EventInterface { + // Mask representing currently busy events + u64 events_mask{}; + // Each kernel event associated to an NV event + std::array<Kernel::EventPair, MaxNvEvents> events; + // The status of the current NVEvent + std::array<EventState, MaxNvEvents> status{}; + // Tells if an NVEvent is registered or not + std::array<bool, MaxNvEvents> registered{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + std::array<u32, MaxNvEvents> assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + std::array<u32, MaxNvEvents> assigned_value{}; + // Constant to denote an unasigned syncpoint. + static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; + std::optional<u32> GetFreeEvent() const { + u64 mask = events_mask; + for (u32 i = 0; i < MaxNvEvents; i++) { + const bool is_free = (mask & 0x1) == 0; + if (is_free) { + if (status[i] == EventState::Registered || status[i] == EventState::Free) { + return {i}; + } + } + mask = mask >> 1; + } + return {}; + } + void SetEventStatus(const u32 event_id, EventState new_status) { + EventState old_status = status[event_id]; + if (old_status == new_status) { + return; + } + status[event_id] = new_status; + if (new_status == EventState::Registered) { + registered[event_id] = true; + } + if (new_status == EventState::Waiting || new_status == EventState::Busy) { + events_mask |= (1ULL << event_id); + } + } + void RegisterEvent(const u32 event_id) { + registered[event_id] = true; + if (status[event_id] == EventState::Free) { + status[event_id] = EventState::Registered; + } + } + void UnregisterEvent(const u32 event_id) { + registered[event_id] = false; + if (status[event_id] == EventState::Registered) { + status[event_id] = EventState::Free; + } + } + void LiberateEvent(const u32 event_id) { + status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; + events_mask &= ~(1ULL << event_id); + assigned_syncpt[event_id] = unassigned_syncpt; + assigned_value[event_id] = 0; + } }; -static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); - class Module final { public: - Module(); + Module(Core::System& system); ~Module(); /// Returns a pointer to one of the available devices, identified by its name. @@ -44,10 +106,18 @@ public: /// Opens a device node and returns a file descriptor to it. u32 Open(const std::string& device_name); /// Sends an ioctl command to the specified file descriptor. - u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output); + u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2, + std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl, + IoctlVersion version); /// Closes a device file descriptor and returns operation success. ResultCode Close(u32 fd); + void SignalSyncpt(const u32 syncpoint_id, const u32 value); + + Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const; + + Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; + private: /// Id to use for the next open file descriptor. u32 next_fd = 1; @@ -57,9 +127,12 @@ private: /// Mapping of device node names to their implementation. std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; + + EventInterface events_interface; }; /// Registers all NVDRV services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system); } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 5731e815f..e1a07d3ee 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -34,7 +34,8 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) buffer_wait_event.writable->Signal(); } -std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { +std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, + u32 height) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { // Only consider free buffers. Buffers become free once again after they've been Acquired // and Released by the compositor, see the NVFlinger::Compose method. @@ -51,7 +52,7 @@ std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) { } itr->status = Buffer::Status::Dequeued; - return itr->slot; + return {{itr->slot, &itr->multi_fence}}; } const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { @@ -63,7 +64,8 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const { } void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect) { + const Common::Rectangle<int>& crop_rect, u32 swap_interval, + Service::Nvidia::MultiFence& multi_fence) { auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { return buffer.slot == slot; }); ASSERT(itr != queue.end()); @@ -71,12 +73,21 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, itr->status = Buffer::Status::Queued; itr->transform = transform; itr->crop_rect = crop_rect; + itr->swap_interval = swap_interval; + itr->multi_fence = multi_fence; + queue_sequence.push_back(slot); } std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { - auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) { - return buffer.status == Buffer::Status::Queued; - }); + auto itr = queue.end(); + // Iterate to find a queued buffer matching the requested slot. + while (itr == queue.end() && !queue_sequence.empty()) { + u32 slot = queue_sequence.front(); + itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { + return buffer.status == Buffer::Status::Queued && buffer.slot == slot; + }); + queue_sequence.pop_front(); + } if (itr == queue.end()) return {}; itr->status = Buffer::Status::Acquired; diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index e1ccb6171..356bedb81 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -4,6 +4,7 @@ #pragma once +#include <list> #include <optional> #include <vector> @@ -12,6 +13,7 @@ #include "common/swap.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" namespace Service::NVFlinger { @@ -68,13 +70,17 @@ public: IGBPBuffer igbp_buffer; BufferTransformFlags transform; Common::Rectangle<int> crop_rect; + u32 swap_interval; + Service::Nvidia::MultiFence multi_fence; }; void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); - std::optional<u32> DequeueBuffer(u32 width, u32 height); + std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width, + u32 height); const IGBPBuffer& RequestBuffer(u32 slot) const; void QueueBuffer(u32 slot, BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect); + const Common::Rectangle<int>& crop_rect, u32 swap_interval, + Service::Nvidia::MultiFence& multi_fence); std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); void ReleaseBuffer(u32 slot); u32 Query(QueryType type); @@ -92,6 +98,7 @@ private: u64 layer_id; std::vector<Buffer> queue; + std::list<u32> queue_sequence; Kernel::EventPair buffer_wait_event; }; diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 3c5c53e24..2e4d707b9 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -29,27 +29,28 @@ namespace Service::NVFlinger { constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); -NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { - displays.emplace_back(0, "Default"); - displays.emplace_back(1, "External"); - displays.emplace_back(2, "Edid"); - displays.emplace_back(3, "Internal"); - displays.emplace_back(4, "Null"); +NVFlinger::NVFlinger(Core::System& system) : system(system) { + displays.emplace_back(0, "Default", system); + displays.emplace_back(1, "External", system); + displays.emplace_back(2, "Edid", system); + displays.emplace_back(3, "Internal", system); + displays.emplace_back(4, "Null", system); // Schedule the screen composition events - const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : frame_ticks; - - composition_event = core_timing.RegisterEvent( - "ScreenComposition", [this, ticks](u64 userdata, s64 cycles_late) { + composition_event = system.CoreTiming().RegisterEvent( + "ScreenComposition", [this](u64 userdata, s64 cycles_late) { Compose(); - this->core_timing.ScheduleEvent(ticks - cycles_late, composition_event); + const auto ticks = + Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); + this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), + composition_event); }); - core_timing.ScheduleEvent(ticks, composition_event); + system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); } NVFlinger::~NVFlinger() { - core_timing.UnscheduleEvent(composition_event, 0); + system.CoreTiming().UnscheduleEvent(composition_event, 0); } void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { @@ -186,11 +187,9 @@ void NVFlinger::Compose() { MicroProfileFlip(); if (!buffer) { - auto& system_instance = Core::System::GetInstance(); - // There was no queued buffer to draw, render previous frame - system_instance.GetPerfStats().EndGameFrame(); - system_instance.GPU().SwapBuffers({}); + system.GetPerfStats().EndGameFrame(); + system.GPU().SwapBuffers({}); continue; } @@ -206,8 +205,14 @@ void NVFlinger::Compose() { igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->get().transform, buffer->get().crop_rect); + swap_interval = buffer->get().swap_interval; buffer_queue.ReleaseBuffer(buffer->get().slot); } } +s64 NVFlinger::GetNextTicks() const { + constexpr s64 max_hertz = 120LL; + return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; +} + } // namespace Service::NVFlinger diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index c0a83fffb..5d7e3bfb8 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -38,7 +38,7 @@ class BufferQueue; class NVFlinger final { public: - explicit NVFlinger(Core::Timing::CoreTiming& core_timing); + explicit NVFlinger(Core::System& system); ~NVFlinger(); /// Sets the NVDrv module instance to use to send buffers to the GPU. @@ -74,6 +74,8 @@ public: /// finished. void Compose(); + s64 GetNextTicks() const; + private: /// Finds the display identified by the specified ID. VI::Display* FindDisplay(u64 display_id); @@ -98,11 +100,12 @@ private: /// layers. u32 next_buffer_queue_id = 1; + u32 swap_interval = 1; + /// Event that handles screen composition. Core::Timing::EventType* composition_event; - /// Core timing instance for registering/unregistering the composition event. - Core::Timing::CoreTiming& core_timing; + Core::System& system; }; } // namespace Service::NVFlinger diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index ebcc41a43..fe6b5f798 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -3,11 +3,44 @@ // Refer to the license.txt file included. #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/pm/pm.h" #include "core/hle/service/service.h" namespace Service::PM { +namespace { + +constexpr ResultCode ERROR_PROCESS_NOT_FOUND{ErrorModule::PM, 1}; + +constexpr u64 NO_PROCESS_FOUND_PID{0}; + +std::optional<Kernel::SharedPtr<Kernel::Process>> SearchProcessList( + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list, + std::function<bool(const Kernel::SharedPtr<Kernel::Process>&)> predicate) { + const auto iter = std::find_if(process_list.begin(), process_list.end(), predicate); + + if (iter == process_list.end()) { + return std::nullopt; + } + + return *iter; +} + +void GetApplicationPidGeneric(Kernel::HLERequestContext& ctx, + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) { + const auto process = SearchProcessList(process_list, [](const auto& process) { + return process->GetProcessID() == Kernel::Process::ProcessIDMin; + }); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(process.has_value() ? (*process)->GetProcessID() : NO_PROCESS_FOUND_PID); +} + +} // Anonymous namespace + class BootMode final : public ServiceFramework<BootMode> { public: explicit BootMode() : ServiceFramework{"pm:bm"} { @@ -41,14 +74,15 @@ private: class DebugMonitor final : public ServiceFramework<DebugMonitor> { public: - explicit DebugMonitor() : ServiceFramework{"pm:dmnt"} { + explicit DebugMonitor(const Kernel::KernelCore& kernel) + : ServiceFramework{"pm:dmnt"}, kernel(kernel) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "GetDebugProcesses"}, {1, nullptr, "StartDebugProcess"}, - {2, nullptr, "GetTitlePid"}, + {2, &DebugMonitor::GetTitlePid, "GetTitlePid"}, {3, nullptr, "EnableDebugForTitleId"}, - {4, nullptr, "GetApplicationPid"}, + {4, &DebugMonitor::GetApplicationPid, "GetApplicationPid"}, {5, nullptr, "EnableDebugForApplication"}, {6, nullptr, "DisableDebug"}, }; @@ -56,21 +90,77 @@ public: RegisterHandlers(functions); } + +private: + void GetTitlePid(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_PM, "called, title_id={:016X}", title_id); + + const auto process = + SearchProcessList(kernel.GetProcessList(), [title_id](const auto& process) { + return process->GetTitleID() == title_id; + }); + + if (!process.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_PROCESS_NOT_FOUND); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push((*process)->GetProcessID()); + } + + void GetApplicationPid(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PM, "called"); + GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + } + + const Kernel::KernelCore& kernel; }; class Info final : public ServiceFramework<Info> { public: - explicit Info() : ServiceFramework{"pm:info"} { + explicit Info(const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list) + : ServiceFramework{"pm:info"}, process_list(process_list) { static const FunctionInfo functions[] = { - {0, nullptr, "GetTitleId"}, + {0, &Info::GetTitleId, "GetTitleId"}, }; RegisterHandlers(functions); } + +private: + void GetTitleId(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); + + const auto process = SearchProcessList(process_list, [process_id](const auto& process) { + return process->GetProcessID() == process_id; + }); + + if (!process.has_value()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_PROCESS_NOT_FOUND); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push((*process)->GetTitleID()); + } + + const std::vector<Kernel::SharedPtr<Kernel::Process>>& process_list; }; class Shell final : public ServiceFramework<Shell> { public: - explicit Shell() : ServiceFramework{"pm:shell"} { + explicit Shell(const Kernel::KernelCore& kernel) + : ServiceFramework{"pm:shell"}, kernel(kernel) { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "LaunchProcess"}, @@ -79,21 +169,31 @@ public: {3, nullptr, "GetProcessEventWaiter"}, {4, nullptr, "GetProcessEventType"}, {5, nullptr, "NotifyBootFinished"}, - {6, nullptr, "GetApplicationPid"}, + {6, &Shell::GetApplicationPid, "GetApplicationPid"}, {7, nullptr, "BoostSystemMemoryResourceLimit"}, {8, nullptr, "EnableAdditionalSystemThreads"}, + {9, nullptr, "GetUnimplementedEventHandle"}, }; // clang-format on RegisterHandlers(functions); } + +private: + void GetApplicationPid(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PM, "called"); + GetApplicationPidGeneric(ctx, kernel.GetProcessList()); + } + + const Kernel::KernelCore& kernel; }; -void InstallInterfaces(SM::ServiceManager& sm) { - std::make_shared<BootMode>()->InstallAsService(sm); - std::make_shared<DebugMonitor>()->InstallAsService(sm); - std::make_shared<Info>()->InstallAsService(sm); - std::make_shared<Shell>()->InstallAsService(sm); +void InstallInterfaces(Core::System& system) { + std::make_shared<BootMode>()->InstallAsService(system.ServiceManager()); + std::make_shared<DebugMonitor>(system.Kernel())->InstallAsService(system.ServiceManager()); + std::make_shared<Info>(system.Kernel().GetProcessList()) + ->InstallAsService(system.ServiceManager()); + std::make_shared<Shell>(system.Kernel())->InstallAsService(system.ServiceManager()); } } // namespace Service::PM diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h index cc8d3f215..852e7050c 100644 --- a/src/core/hle/service/pm/pm.h +++ b/src/core/hle/service/pm/pm.h @@ -4,8 +4,8 @@ #pragma once -namespace Service::SM { -class ServiceManager; +namespace Core { +class System; } namespace Service::PM { @@ -16,6 +16,6 @@ enum class SystemBootMode { }; /// Registers all PM services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::PM diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index e4fcee9f8..18d895263 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -2,26 +2,31 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/hex_util.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" +#include "core/hle/service/acc/profile_manager.h" #include "core/hle/service/prepo/prepo.h" #include "core/hle/service/service.h" +#include "core/reporter.h" namespace Service::PlayReport { class PlayReport final : public ServiceFramework<PlayReport> { public: - explicit PlayReport(const char* name) : ServiceFramework{name} { + explicit PlayReport(const char* name, Core::System& system) + : ServiceFramework{name}, system(system) { // clang-format off static const FunctionInfo functions[] = { - {10100, nullptr, "SaveReportOld"}, - {10101, &PlayReport::SaveReportWithUserOld, "SaveReportWithUserOld"}, - {10102, nullptr, "SaveReport"}, - {10103, nullptr, "SaveReportWithUser"}, + {10100, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old>, "SaveReportOld"}, + {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, + {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"}, + {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"}, {10200, nullptr, "RequestImmediateTransmission"}, {10300, nullptr, "GetTransmissionStatus"}, - {20100, nullptr, "SaveSystemReport"}, - {20101, nullptr, "SaveSystemReportWithUser"}, + {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, + {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, {20200, nullptr, "SetOperationMode"}, {30100, nullptr, "ClearStorage"}, {30200, nullptr, "ClearStatistics"}, @@ -39,21 +44,96 @@ public: } private: - void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { - // TODO(ogniK): Do we want to add play report? - LOG_WARNING(Service_PREPO, "(STUBBED) called"); + template <Core::Reporter::PlayReportType Type> + void SaveReport(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto process_id = rp.PopRaw<u64>(); + + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = ctx.ReadBuffer(1); + + LOG_DEBUG(Service_PREPO, + "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", + static_cast<u8>(Type), process_id, data1.size(), data2.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, + process_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + template <Core::Reporter::PlayReportType Type> + void SaveReportWithUser(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto user_id = rp.PopRaw<u128>(); + const auto process_id = rp.PopRaw<u64>(); + + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = ctx.ReadBuffer(1); + + LOG_DEBUG( + Service_PREPO, + "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " + "data2_size={:016X}", + static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, + process_id, user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SaveSystemReport(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto title_id = rp.PopRaw<u64>(); + + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = ctx.ReadBuffer(1); + + LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", + title_id, data1.size(), data2.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void SaveSystemReportWithUser(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto user_id = rp.PopRaw<u128>(); + const auto title_id = rp.PopRaw<u64>(); + + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = ctx.ReadBuffer(1); + + LOG_DEBUG(Service_PREPO, + "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " + "data2_size={:016X}", + user_id[1], user_id[0], title_id, data1.size(), data2.size()); + + const auto& reporter{system.GetReporter()}; + reporter.SavePlayReport(Core::Reporter::PlayReportType::System, title_id, {data1, data2}, + std::nullopt, user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + + Core::System& system; }; -void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared<PlayReport>("prepo:a")->InstallAsService(service_manager); - std::make_shared<PlayReport>("prepo:a2")->InstallAsService(service_manager); - std::make_shared<PlayReport>("prepo:m")->InstallAsService(service_manager); - std::make_shared<PlayReport>("prepo:s")->InstallAsService(service_manager); - std::make_shared<PlayReport>("prepo:u")->InstallAsService(service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { + std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager); + std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager); } } // namespace Service::PlayReport diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h index 0e7b01331..a5682ee26 100644 --- a/src/core/hle/service/prepo/prepo.h +++ b/src/core/hle/service/prepo/prepo.h @@ -8,8 +8,12 @@ namespace Service::SM { class ServiceManager; } +namespace Core { +class System; +} + namespace Service::PlayReport { -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); } // namespace Service::PlayReport diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 00806b0ed..831a427de 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -19,7 +19,6 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" -#include "core/hle/service/arp/arp.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/bcat/module.h" #include "core/hle/service/bpc/bpc.h" @@ -33,6 +32,7 @@ #include "core/hle/service/fgm/fgm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/friend/friend.h" +#include "core/hle/service/glue/glue.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lbl/lbl.h" @@ -68,6 +68,7 @@ #include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/wlan/wlan.h" +#include "core/reporter.h" namespace Service { @@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext } buf.push_back('}'); + Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport( + ctx, ctx.GetCommand(), function_name, service_name); UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); } @@ -192,61 +195,61 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co // Module interface /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, - FileSys::VfsFilesystem& vfs) { +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. - auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); + auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); + system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); SM::ServiceManager::InstallInterfaces(sm); - Account::InstallInterfaces(*sm); - AM::InstallInterfaces(*sm, nv_flinger); - AOC::InstallInterfaces(*sm); - APM::InstallInterfaces(*sm); - ARP::InstallInterfaces(*sm); - Audio::InstallInterfaces(*sm); + Account::InstallInterfaces(system); + AM::InstallInterfaces(*sm, nv_flinger, system); + AOC::InstallInterfaces(*sm, system); + APM::InstallInterfaces(system); + Audio::InstallInterfaces(*sm, system); BCAT::InstallInterfaces(*sm); BPC::InstallInterfaces(*sm); - BtDrv::InstallInterfaces(*sm); - BTM::InstallInterfaces(*sm); + BtDrv::InstallInterfaces(*sm, system); + BTM::InstallInterfaces(*sm, system); Capture::InstallInterfaces(*sm); ERPT::InstallInterfaces(*sm); ES::InstallInterfaces(*sm); EUPLD::InstallInterfaces(*sm); - Fatal::InstallInterfaces(*sm); + Fatal::InstallInterfaces(*sm, system); FGM::InstallInterfaces(*sm); - FileSystem::InstallInterfaces(*sm, vfs); - Friend::InstallInterfaces(*sm); + FileSystem::InstallInterfaces(system); + Friend::InstallInterfaces(*sm, system); + Glue::InstallInterfaces(system); GRC::InstallInterfaces(*sm); - HID::InstallInterfaces(*sm); + HID::InstallInterfaces(*sm, system); LBL::InstallInterfaces(*sm); LDN::InstallInterfaces(*sm); - LDR::InstallInterfaces(*sm); + LDR::InstallInterfaces(*sm, system); LM::InstallInterfaces(*sm); Migration::InstallInterfaces(*sm); Mii::InstallInterfaces(*sm); MM::InstallInterfaces(*sm); NCM::InstallInterfaces(*sm); NFC::InstallInterfaces(*sm); - NFP::InstallInterfaces(*sm); - NIFM::InstallInterfaces(*sm); - NIM::InstallInterfaces(*sm); + NFP::InstallInterfaces(*sm, system); + NIFM::InstallInterfaces(*sm, system); + NIM::InstallInterfaces(*sm, system); NPNS::InstallInterfaces(*sm); - NS::InstallInterfaces(*sm); - Nvidia::InstallInterfaces(*sm, *nv_flinger); + NS::InstallInterfaces(*sm, system); + Nvidia::InstallInterfaces(*sm, *nv_flinger, system); PCIe::InstallInterfaces(*sm); PCTL::InstallInterfaces(*sm); PCV::InstallInterfaces(*sm); - PlayReport::InstallInterfaces(*sm); - PM::InstallInterfaces(*sm); + PlayReport::InstallInterfaces(*sm, system); + PM::InstallInterfaces(system); PSC::InstallInterfaces(*sm); PSM::InstallInterfaces(*sm); Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); SSL::InstallInterfaces(*sm); - Time::InstallInterfaces(*sm); + Time::InstallInterfaces(system); USB::InstallInterfaces(*sm); VI::InstallInterfaces(*sm, nv_flinger); WLAN::InstallInterfaces(*sm); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index abbfe5524..aef964861 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -18,10 +18,6 @@ namespace Core { class System; } -namespace FileSys { -class VfsFilesystem; -} - namespace Kernel { class ClientPort; class ServerPort; @@ -31,6 +27,10 @@ class HLERequestContext; namespace Service { +namespace FileSystem { +class FileSystemController; +} // namespace FileSystem + namespace SM { class ServiceManager; } @@ -182,8 +182,7 @@ private: }; /// Initialize ServiceManager -void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system, - FileSys::VfsFilesystem& vfs); +void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 298d85011..b54214421 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -95,6 +95,14 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) { PushResponseLanguageCode(ctx, post4_0_0_max_entries); } +void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(Settings::values.quest_flag)); +} + void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); @@ -114,7 +122,7 @@ SET::SET() : ServiceFramework("set") { {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, {7, nullptr, "GetKeyCodeMap"}, - {8, nullptr, "GetQuestFlag"}, + {8, &SET::GetQuestFlag, "GetQuestFlag"}, {9, nullptr, "GetKeyCodeMap2"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 31f9cb296..b154e08aa 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -42,6 +42,7 @@ private: void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); + void GetQuestFlag(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 8d122ae33..9565e7de5 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -6,8 +6,9 @@ namespace Service::Time { -Time::Time(std::shared_ptr<Module> time, const char* name) - : Module::Interface(std::move(time), name) { +Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, + Core::System& system, const char* name) + : Module::Interface(std::move(time), std::move(shared_memory), system, name) { // clang-format off static const FunctionInfo functions[] = { {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, @@ -16,12 +17,12 @@ Time::Time(std::shared_ptr<Module> time, const char* name) {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, {5, nullptr, "GetEphemeralNetworkSystemClock"}, - {20, nullptr, "GetSharedMemoryNativeHandle"}, + {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"}, {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, {50, nullptr, "SetStandardSteadyClockInternalOffset"}, - {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, - {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, + {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, + {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, {102, nullptr, "GetStandardUserSystemClockInitialYear"}, {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h index cd6b44dec..5c63a07f4 100644 --- a/src/core/hle/service/time/interface.h +++ b/src/core/hle/service/time/interface.h @@ -8,9 +8,12 @@ namespace Service::Time { +class SharedMemory; + class Time final : public Module::Interface { public: - explicit Time(std::shared_ptr<Module> time, const char* name); + explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, + Core::System& system, const char* name); ~Time() override; }; diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 346bad80d..1b9ab8401 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -13,6 +13,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/service/time/interface.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/time/time_sharedmemory.h" #include "core/settings.h" namespace Service::Time { @@ -61,9 +62,18 @@ static u64 CalendarToPosix(const CalendarTime& calendar_time, return static_cast<u64>(epoch_time); } +enum class ClockContextType { + StandardSteady, + StandardUserSystem, + StandardNetworkSystem, + StandardLocalSystem, +}; + class ISystemClock final : public ServiceFramework<ISystemClock> { public: - ISystemClock() : ServiceFramework("ISystemClock") { + ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory, + ClockContextType clock_type) + : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) { static const FunctionInfo functions[] = { {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, {1, nullptr, "SetCurrentTime"}, @@ -72,6 +82,8 @@ public: }; RegisterHandlers(functions); + + UpdateSharedMemoryContext(system_clock_context); } private: @@ -87,34 +99,64 @@ private: void GetSystemClockContext(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_Time, "(STUBBED) called"); - SystemClockContext system_clock_ontext{}; + // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll + // only update when we get a new context + UpdateSharedMemoryContext(system_clock_context); + IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(system_clock_ontext); + rb.PushRaw(system_clock_context); } + + void UpdateSharedMemoryContext(const SystemClockContext& clock_context) { + switch (clock_type) { + case ClockContextType::StandardLocalSystem: + shared_memory->SetStandardLocalSystemClockContext(clock_context); + break; + case ClockContextType::StandardNetworkSystem: + shared_memory->SetStandardNetworkSystemClockContext(clock_context); + break; + } + } + + SystemClockContext system_clock_context{}; + std::shared_ptr<Service::Time::SharedMemory> shared_memory; + ClockContextType clock_type; }; class ISteadyClock final : public ServiceFramework<ISteadyClock> { public: - ISteadyClock() : ServiceFramework("ISteadyClock") { + ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system) + : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) { static const FunctionInfo functions[] = { {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, }; RegisterHandlers(functions); + + shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint()); } private: void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); - const auto& core_timing = Core::System::GetInstance().CoreTiming(); - const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); - const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), - {}}; + const auto time_point = GetCurrentTimePoint(); + // TODO(ogniK): This should be updated periodically + shared_memory->SetStandardSteadyClockTimepoint(time_point); + IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; rb.Push(RESULT_SUCCESS); - rb.PushRaw(steady_clock_time_point); + rb.PushRaw(time_point); + } + + SteadyClockTimePoint GetCurrentTimePoint() const { + const auto& core_timing = system.CoreTiming(); + const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); + return {static_cast<u64_le>(ms.count() / 1000), {}}; } + + std::shared_ptr<SharedMemory> shared_memory; + Core::System& system; }; class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { @@ -233,7 +275,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem); } void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { @@ -241,7 +283,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem); } void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { @@ -249,7 +291,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISteadyClock>(); + rb.PushIpcInterface<ISteadyClock>(shared_memory, system); } void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { @@ -265,7 +307,7 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<ISystemClock>(); + rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem); } void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { @@ -284,7 +326,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { return; } - const auto& core_timing = Core::System::GetInstance().CoreTiming(); + const auto& core_timing = system.CoreTiming(); const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; @@ -333,16 +375,56 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( rb.PushRaw<u64>(difference); } -Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) - : ServiceFramework(name), time(std::move(time)) {} +void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder()); +} + +void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled( + Kernel::HLERequestContext& ctx) { + // ogniK(TODO): When clock contexts are implemented, the value should be read from the context + // instead of our shared memory holder + LOG_DEBUG(Service_Time, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled()); +} + +void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto enabled = rp.Pop<u8>(); + + LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called"); + + // TODO(ogniK): Update clock contexts and correct timespans + + shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +Module::Interface::Interface(std::shared_ptr<Module> time, + std::shared_ptr<SharedMemory> shared_memory, Core::System& system, + const char* name) + : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)), + system(system) {} Module::Interface::~Interface() = default; -void InstallInterfaces(SM::ServiceManager& service_manager) { +void InstallInterfaces(Core::System& system) { auto time = std::make_shared<Module>(); - std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); - std::make_shared<Time>(time, "time:s")->InstallAsService(service_manager); - std::make_shared<Time>(time, "time:u")->InstallAsService(service_manager); + auto shared_mem = std::make_shared<SharedMemory>(system); + + std::make_shared<Time>(time, shared_mem, system, "time:a") + ->InstallAsService(system.ServiceManager()); + std::make_shared<Time>(time, shared_mem, system, "time:s") + ->InstallAsService(system.ServiceManager()); + std::make_shared<Time>(std::move(time), shared_mem, system, "time:u") + ->InstallAsService(system.ServiceManager()); } } // namespace Service::Time diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index f11affe95..c32d32860 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -10,6 +10,8 @@ namespace Service::Time { +class SharedMemory; + struct LocationName { std::array<u8, 0x24> name; }; @@ -77,7 +79,9 @@ class Module final { public: class Interface : public ServiceFramework<Interface> { public: - explicit Interface(std::shared_ptr<Module> time, const char* name); + explicit Interface(std::shared_ptr<Module> time, + std::shared_ptr<SharedMemory> shared_memory, Core::System& system, + const char* name); ~Interface() override; void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); @@ -87,13 +91,18 @@ public: void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); void GetClockSnapshot(Kernel::HLERequestContext& ctx); void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); + void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); + void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); + void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx); protected: std::shared_ptr<Module> time; + std::shared_ptr<SharedMemory> shared_memory; + Core::System& system; }; }; /// Registers all Time services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager); +void InstallInterfaces(Core::System& system); } // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp new file mode 100644 index 000000000..bfc81b83c --- /dev/null +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -0,0 +1,68 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/hle/service/time/time_sharedmemory.h" + +namespace Service::Time { +const std::size_t SHARED_MEMORY_SIZE = 0x1000; + +SharedMemory::SharedMemory(Core::System& system) : system(system) { + shared_memory_holder = Kernel::SharedMemory::Create( + system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, + Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory"); + + // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash + // if it's set to anything else + shared_memory_format.format_version = 14; + std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format)); +} + +SharedMemory::~SharedMemory() = default; + +Kernel::SharedPtr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const { + return shared_memory_holder; +} + +void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) { + shared_memory_format.standard_steady_clock_timepoint.StoreData( + shared_memory_holder->GetPointer(), timepoint); +} + +void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) { + shared_memory_format.standard_local_system_clock_context.StoreData( + shared_memory_holder->GetPointer(), context); +} + +void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) { + shared_memory_format.standard_network_system_clock_context.StoreData( + shared_memory_holder->GetPointer(), context); +} + +void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) { + shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( + shared_memory_holder->GetPointer(), enabled); +} + +SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() { + return shared_memory_format.standard_steady_clock_timepoint.ReadData( + shared_memory_holder->GetPointer()); +} + +SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() { + return shared_memory_format.standard_local_system_clock_context.ReadData( + shared_memory_holder->GetPointer()); +} + +SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() { + return shared_memory_format.standard_network_system_clock_context.ReadData( + shared_memory_holder->GetPointer()); +} + +bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() { + return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData( + shared_memory_holder->GetPointer()); +} + +} // namespace Service::Time diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h new file mode 100644 index 000000000..cb8253541 --- /dev/null +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -0,0 +1,74 @@ +// Copyright 2019 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/time/time.h" + +namespace Service::Time { +class SharedMemory { +public: + explicit SharedMemory(Core::System& system); + ~SharedMemory(); + + // Return the shared memory handle + Kernel::SharedPtr<Kernel::SharedMemory> GetSharedMemoryHolder() const; + + // Set memory barriers in shared memory and update them + void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint); + void SetStandardLocalSystemClockContext(const SystemClockContext& context); + void SetStandardNetworkSystemClockContext(const SystemClockContext& context); + void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled); + + // Pull from memory barriers in the shared memory + SteadyClockTimePoint GetStandardSteadyClockTimepoint(); + SystemClockContext GetStandardLocalSystemClockContext(); + SystemClockContext GetStandardNetworkSystemClockContext(); + bool GetStandardUserSystemClockAutomaticCorrectionEnabled(); + + // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this? + template <typename T, std::size_t Offset> + struct MemoryBarrier { + static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable"); + u32_le read_attempt{}; + std::array<T, 2> data{}; + + // These are not actually memory barriers at the moment as we don't have multicore and all + // HLE is mutexed. This will need to properly be implemented when we start updating the time + // points on threads. As of right now, we'll be updated both values synchronously and just + // incrementing the read_attempt to indicate that we waited. + void StoreData(u8* shared_memory, T data_to_store) { + std::memcpy(this, shared_memory + Offset, sizeof(*this)); + read_attempt++; + data[read_attempt & 1] = data_to_store; + std::memcpy(shared_memory + Offset, this, sizeof(*this)); + } + + // For reading we're just going to read the last stored value. If there was no value stored + // it will just end up reading an empty value as intended. + T ReadData(u8* shared_memory) { + std::memcpy(this, shared_memory + Offset, sizeof(*this)); + return data[(read_attempt - 1) & 1]; + } + }; + + // Shared memory format + struct Format { + MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint; + MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context; + MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context; + MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction; + u32_le format_version; + }; + static_assert(sizeof(Format) == 0xd8, "Format is an invalid size"); + +private: + Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_holder{}; + Core::System& system; + Format shared_memory_format{}; +}; + +} // namespace Service::Time diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index a8d088305..006a6d9ff 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -15,8 +15,8 @@ namespace Service::VI { -Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { - auto& kernel = Core::System::GetInstance().Kernel(); +Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} { + auto& kernel = system.Kernel(); vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, fmt::format("Display VSync Event {}", id)); } diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 2acd46ff8..f56b5badc 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -26,7 +26,7 @@ public: /// @param id The unique ID for this display. /// @param name The name for this display. /// - Display(u64 id, std::string name); + Display(u64 id, std::string name, Core::System& system); ~Display(); Display(const Display&) = delete; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index f1fa6ccd1..199b30635 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -21,6 +21,7 @@ #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -328,32 +329,22 @@ public: Data data; }; -struct BufferProducerFence { - u32 is_valid; - std::array<Nvidia::IoctlFence, 4> fences; -}; -static_assert(sizeof(BufferProducerFence) == 36, "BufferProducerFence has wrong size"); - class IGBPDequeueBufferResponseParcel : public Parcel { public: - explicit IGBPDequeueBufferResponseParcel(u32 slot) : slot(slot) {} + explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence) + : slot(slot), multi_fence(multi_fence) {} ~IGBPDequeueBufferResponseParcel() override = default; protected: void SerializeData() override { - // TODO(Subv): Find out how this Fence is used. - BufferProducerFence fence = {}; - fence.is_valid = 1; - for (auto& fence_ : fence.fences) - fence_.id = -1; - Write(slot); Write<u32_le>(1); - WriteObject(fence); + WriteObject(multi_fence); Write<u32_le>(0); } u32_le slot; + Service::Nvidia::MultiFence multi_fence; }; class IGBPRequestBufferRequestParcel : public Parcel { @@ -400,12 +391,6 @@ public: data = Read<Data>(); } - struct Fence { - u32_le id; - u32_le value; - }; - static_assert(sizeof(Fence) == 8, "Fence has wrong size"); - struct Data { u32_le slot; INSERT_PADDING_WORDS(3); @@ -418,15 +403,15 @@ public: s32_le scaling_mode; NVFlinger::BufferQueue::BufferTransformFlags transform; u32_le sticky_transform; - INSERT_PADDING_WORDS(2); - u32_le fence_is_valid; - std::array<Fence, 2> fences; + INSERT_PADDING_WORDS(1); + u32_le swap_interval; + Service::Nvidia::MultiFence multi_fence; Common::Rectangle<int> GetCropRect() const { return {crop_left, crop_top, crop_right, crop_bottom}; } }; - static_assert(sizeof(Data) == 80, "ParcelData has wrong size"); + static_assert(sizeof(Data) == 96, "ParcelData has wrong size"); Data data; }; @@ -547,11 +532,11 @@ private: IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; const u32 width{request.data.width}; const u32 height{request.data.height}; - std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); + auto result = buffer_queue.DequeueBuffer(width, height); - if (slot) { + if (result) { // Buffer is available - IGBPDequeueBufferResponseParcel response{*slot}; + IGBPDequeueBufferResponseParcel response{result->first, *result->second}; ctx.WriteBuffer(response.Serialize()); } else { // Wait the current thread until a buffer becomes available @@ -561,10 +546,10 @@ private: Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available auto& buffer_queue = nv_flinger->FindBufferQueue(id); - std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height); - ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); + auto result = buffer_queue.DequeueBuffer(width, height); + ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); - IGBPDequeueBufferResponseParcel response{*slot}; + IGBPDequeueBufferResponseParcel response{result->first, *result->second}; ctx.WriteBuffer(response.Serialize()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -582,7 +567,8 @@ private: IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; buffer_queue.QueueBuffer(request.data.slot, request.data.transform, - request.data.GetCropRect()); + request.data.GetCropRect(), request.data.swap_interval, + request.data.multi_fence); IGBPQueueBufferResponseParcel response{1280, 720}; ctx.WriteBuffer(response.Serialize()); |