From e9a1f29e936e8da5899c360394f37c7646150e3f Mon Sep 17 00:00:00 2001 From: Chloe Marcec Date: Sat, 27 Mar 2021 02:03:18 +1100 Subject: [PATCH] pctl: Rework how pctl works to be more accurate Introduces the usage of compatibilities to allow it the module to be closer to how it works on hardware. --- src/core/file_sys/control_metadata.cpp | 8 + src/core/file_sys/control_metadata.h | 2 + src/core/hle/service/pctl/module.cpp | 249 ++++++++++++++++++++++--- src/core/hle/service/pctl/module.h | 19 +- src/core/hle/service/pctl/pctl.cpp | 5 +- src/core/hle/service/pctl/pctl.h | 3 +- 6 files changed, 254 insertions(+), 32 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index b0a130345..f66759815 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -100,6 +100,14 @@ u64 NACP::GetDeviceSaveDataSize() const { return raw.device_save_data_size; } +u32 NACP::GetParentalControlFlag() const { + return raw.parental_control; +} + +const std::array& NACP::GetRatingAge() const { + return raw.rating_age; +} + std::vector NACP::GetRawBytes() const { std::vector out(sizeof(RawNACP)); std::memcpy(out.data(), &raw, sizeof(RawNACP)); diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 403c4219a..dd9837cf5 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -114,6 +114,8 @@ public: std::vector GetRawBytes() const; bool GetUserAccountSwitchLock() const; u64 GetDeviceSaveDataSize() const; + u32 GetParentalControlFlag() const; + const std::array& GetRatingAge() const; private: RawNACP raw{}; diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index f9089bf2f..2f0edde2a 100644 --- a/src/core/hle/service/pctl/module.cpp +++ b/src/core/hle/service/pctl/module.cpp @@ -3,16 +3,30 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/pctl/module.h" #include "core/hle/service/pctl/pctl.h" namespace Service::PCTL { +namespace Error { + +constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101}; +constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104}; +constexpr ResultCode ResultNoCapatability{ErrorModule::PCTL, 131}; +constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181}; + +} // namespace Error + class IParentalControlService final : public ServiceFramework { public: - explicit IParentalControlService(Core::System& system_) - : ServiceFramework{system_, "IParentalControlService"} { + explicit IParentalControlService(Core::System& system_, Capability capability) + : ServiceFramework{system_, "IParentalControlService"}, system(system_), + capability(capability) { // clang-format off static const FunctionInfo functions[] = { {1, &IParentalControlService::Initialize, "Initialize"}, @@ -28,13 +42,13 @@ public: {1010, nullptr, "IsRestrictedSystemSettingsEntered"}, {1011, nullptr, "RevertRestrictedSystemSettingsEntered"}, {1012, nullptr, "GetRestrictedFeatures"}, - {1013, nullptr, "ConfirmStereoVisionPermission"}, + {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"}, {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, {1015, nullptr, "ConfirmPlayableApplicationVideo"}, {1016, nullptr, "ConfirmShowNewsPermission"}, {1017, nullptr, "EndFreeCommunication"}, - {1018, nullptr, "IsFreeCommunicationAvailable"}, - {1031, nullptr, "IsRestrictionEnabled"}, + {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"}, + {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"}, {1032, nullptr, "GetSafetyLevel"}, {1033, nullptr, "SetSafetyLevel"}, {1034, nullptr, "GetSafetyLevelSettings"}, @@ -119,62 +133,234 @@ public: } private: - void Initialize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + bool CheckFreeCommunicationPermissionImpl() { + if (states.temporary_unlocked) { + return true; + } + if ((states.application_info.parental_control_flag & 1) == 0) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_free_communication_default_on) { + return true; + } + // TODO(ogniK): Check for blacklisted/exempted applications + return true; + } + + bool ConfirmStereoVisionPermissionImpl() { + if (states.temporary_unlocked) { + return true; + } + if (pin_code[0] == '\0') { + return true; + } + if (!settings.is_stero_vision_restricted) { + return false; + } + return true; + } + + void SetStereoVisionRestrictionImpl(bool is_restricted) { + if (settings.disabled) { + return; + } + + if (pin_code[0] == '\0') { + return; + } + settings.is_stero_vision_restricted = is_restricted; + } + + void Initialize(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & (Capability::Application | Capability::System))) { + LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", + static_cast(capability)); + return; + } + + // TODO(ogniK): Recovery + + const auto tid = system.CurrentProcess()->GetTitleID(); + if (tid != 0) { + const FileSys::PatchManager pm{tid, system.GetFileSystemController(), + system.GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + if (control.first) { + states.tid_from_event = 0; + states.launch_time_valid = false; + states.is_suspended = false; + states.free_communication = false; + states.stereo_vision = false; + states.application_info = ApplicationInfo{ + .tid = tid, + .age_rating = control.first->GetRatingAge(), + .parental_control_flag = control.first->GetParentalControlFlag(), + .capability = capability, + }; + + if (False(capability & (Capability::System | Capability::Recovery))) { + // TODO(ogniK): Signal application launch event + } + } + } - IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(RESULT_SUCCESS); } void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(RESULT_SUCCESS); + } + + states.free_communication = true; + } + + void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + states.stereo_vision = true; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } - void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { + void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_PCTL, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; + if (!CheckFreeCommunicationPermissionImpl()) { + rb.Push(Error::ResultNoFreeCommunication); + } else { + rb.Push(RESULT_SUCCESS); + } + } + + void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & (Capability::Status | Capability::Recovery))) { + LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!"); + rb.Push(Error::ResultNoCapatability); + rb.Push(false); + return; + } + + rb.Push(pin_code[0] != '\0'); + } + + void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_PCTL, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + + if (False(capability & Capability::SteroVision)) { + LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!"); + rb.Push(Error::ResultNoCapatability); + return; + } + + if (pin_code[0] == '\0') { + rb.Push(Error::ResultNoRestrictionEnabled); + return; + } + rb.Push(RESULT_SUCCESS); } void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(true); + if (!ConfirmStereoVisionPermissionImpl()) { + rb.Push(Error::ResultStereoVisionRestricted); + rb.Push(false); + } else { + rb.Push(RESULT_SUCCESS); + rb.Push(true); + } } void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto can_use = rp.Pop(); - LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use); - - can_use_stereo_vision = can_use; + LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use); IPC::ResponseBuilder rb{ctx, 2}; + if (False(capability & Capability::SteroVision)) { + LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!"); + rb.Push(Error::ResultNoCapatability); + return; + } + + SetStereoVisionRestrictionImpl(can_use); rb.Push(RESULT_SUCCESS); } void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); IPC::ResponseBuilder rb{ctx, 3}; + if (False(capability & Capability::SteroVision)) { + LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!"); + rb.Push(Error::ResultNoCapatability); + rb.Push(false); + return; + } + rb.Push(RESULT_SUCCESS); - rb.Push(can_use_stereo_vision); + rb.Push(settings.is_stero_vision_restricted); } void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_PCTL, "(STUBBED) called"); + LOG_DEBUG(Service_PCTL, "called"); + + states.stereo_vision = false; IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + struct ApplicationInfo { + u64 tid{}; + std::array age_rating{}; + u32 parental_control_flag{}; + Capability capability{}; + }; + + struct States { + u64 current_tid{}; + ApplicationInfo application_info{}; + u64 tid_from_event{}; + bool launch_time_valid{}; + bool is_suspended{}; + bool temporary_unlocked{}; + bool free_communication{}; + bool stereo_vision{}; + }; + + struct ParentalControlSettings { + bool is_stero_vision_restricted{}; + bool is_free_communication_default_on{}; + bool disabled{}; + }; + + States states{}; + ParentalControlSettings settings{}; + std::array pin_code{}; bool can_use_stereo_vision = true; + Core::System& system; + Capability capability{}; }; void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { @@ -182,7 +368,9 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(system); + // TODO(ogniK): Get TID from process + + rb.PushIpcInterface(system, capability); } void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { @@ -190,21 +378,28 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface(system); + rb.PushIpcInterface(system, capability); } Module::Interface::Interface(Core::System& system_, std::shared_ptr module_, - const char* name) - : ServiceFramework{system_, name}, module{std::move(module_)} {} + const char* name, Capability capability) + : ServiceFramework{system_, name}, module{std::move(module_)}, capability(capability) {} Module::Interface::~Interface() = default; void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { auto module = std::make_shared(); - std::make_shared(system, module, "pctl")->InstallAsService(service_manager); - std::make_shared(system, module, "pctl:a")->InstallAsService(service_manager); - std::make_shared(system, module, "pctl:r")->InstallAsService(service_manager); - std::make_shared(system, module, "pctl:s")->InstallAsService(service_manager); + std::make_shared(system, module, "pctl", + Capability::Application | Capability::SnsPost | Capability::Status | + Capability::SteroVision) + ->InstallAsService(service_manager); + // TODO(ogniK): Implement remaining capabilities + std::make_shared(system, module, "pctl:a", Capability::None) + ->InstallAsService(service_manager); + std::make_shared(system, module, "pctl:r", Capability::None) + ->InstallAsService(service_manager); + std::make_shared(system, module, "pctl:s", Capability::None) + ->InstallAsService(service_manager); } } // namespace Service::PCTL diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/module.h index 4c7e09a3b..cdba11d60 100644 --- a/src/core/hle/service/pctl/module.h +++ b/src/core/hle/service/pctl/module.h @@ -4,6 +4,7 @@ #pragma once +#include "common/common_funcs.h" #include "core/hle/service/service.h" namespace Core { @@ -12,12 +13,23 @@ class System; namespace Service::PCTL { +enum class Capability : s32 { + None = 0x0, + Application = 1 << 0, + SnsPost = 1 << 1, + Recovery = 1 << 6, + Status = 1 << 8, + SteroVision = 1 << 9, + System = 1 << 15, +}; +DECLARE_ENUM_FLAG_OPERATORS(Capability); + class Module final { public: class Interface : public ServiceFramework { public: - explicit Interface(Core::System& system_, std::shared_ptr module_, - const char* name); + explicit Interface(Core::System& system_, std::shared_ptr module_, const char* name, + Capability capability); ~Interface() override; void CreateService(Kernel::HLERequestContext& ctx); @@ -25,6 +37,9 @@ public: protected: std::shared_ptr module; + + private: + Capability capability{}; }; }; diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp index 16dd34f90..e4d155c86 100644 --- a/src/core/hle/service/pctl/pctl.cpp +++ b/src/core/hle/service/pctl/pctl.cpp @@ -6,8 +6,9 @@ namespace Service::PCTL { -PCTL::PCTL(Core::System& system_, std::shared_ptr module_, const char* name) - : Interface{system_, std::move(module_), name} { +PCTL::PCTL(Core::System& system_, std::shared_ptr module_, const char* name, + Capability capability) + : Interface{system_, std::move(module_), name, capability} { static const FunctionInfo functions[] = { {0, &PCTL::CreateService, "CreateService"}, {1, &PCTL::CreateServiceWithoutInitialize, "CreateServiceWithoutInitialize"}, diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h index 275d23007..fd0a1e486 100644 --- a/src/core/hle/service/pctl/pctl.h +++ b/src/core/hle/service/pctl/pctl.h @@ -14,7 +14,8 @@ namespace Service::PCTL { class PCTL final : public Module::Interface { public: - explicit PCTL(Core::System& system_, std::shared_ptr module_, const char* name); + explicit PCTL(Core::System& system_, std::shared_ptr module_, const char* name, + Capability capability); ~PCTL() override; };