diff --git a/README.md b/README.md index 7090e7410..42dfe3285 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1533. +This is the source code for early-access 1534. ## Legal Notice diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index b0a130345..f66759815 100755 --- 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 100755 --- 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/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 72a877d68..0a6621ef2 100755 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -133,7 +133,7 @@ private: void GetBlockedUserListIds(Kernel::HLERequestContext& ctx) { // This is safe to stub, as there should be no adverse consequences from reporting no // blocked users. - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_Friend, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(0); // Indicates there are no blocked users @@ -141,14 +141,14 @@ private: void DeclareCloseOnlinePlaySession(Kernel::HLERequestContext& ctx) { // Stub used by Splatoon 2 - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_Friend, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void UpdateUserPresence(Kernel::HLERequestContext& ctx) { // Stub used by Retro City Rampage - LOG_WARNING(Service_ACC, "(STUBBED) called"); + LOG_WARNING(Service_Friend, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } @@ -159,7 +159,7 @@ private: const auto uuid = rp.PopRaw(); [[maybe_unused]] const auto filter = rp.PopRaw(); const auto pid = rp.Pop(); - LOG_WARNING(Service_ACC, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, + LOG_WARNING(Service_Friend, "(STUBBED) called, offset={}, uuid={}, pid={}", friend_offset, uuid.Format(), pid); IPC::ResponseBuilder rb{ctx, 3}; @@ -191,7 +191,7 @@ public: private: void GetEvent(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_Friend, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -199,7 +199,7 @@ private: } void Clear(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_Friend, "called"); while (!notifications.empty()) { notifications.pop(); } @@ -210,10 +210,10 @@ private: } void Pop(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_Friend, "called"); if (notifications.empty()) { - LOG_ERROR(Service_ACC, "No notifications in queue!"); + LOG_ERROR(Service_Friend, "No notifications in queue!"); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ERR_NO_NOTIFICATIONS); return; @@ -231,7 +231,8 @@ private: break; default: // HOS seems not have an error case for an unknown notification - LOG_WARNING(Service_ACC, "Unknown notification {:08X}", notification.notification_type); + LOG_WARNING(Service_Friend, "Unknown notification {:08X}", + notification.notification_type); break; } @@ -269,14 +270,14 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface(system); - LOG_DEBUG(Service_ACC, "called"); + LOG_DEBUG(Service_Friend, "called"); } void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; auto uuid = rp.PopRaw(); - LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format()); + LOG_DEBUG(Service_Friend, "called, uuid={}", uuid.Format()); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index e7063f8ef..93c43a203 100755 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -4,6 +4,7 @@ #include #include "common/common_types.h" +#include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/hle/service/hid/controllers/gesture.h" @@ -19,9 +20,9 @@ Controller_Gesture::~Controller_Gesture() = default; void Controller_Gesture::OnInit() { for (std::size_t id = 0; id < MAX_FINGERS; ++id) { - mouse_finger_id[id] = MAX_FINGERS; - keyboard_finger_id[id] = MAX_FINGERS; - udp_finger_id[id] = MAX_FINGERS; + mouse_finger_id[id] = MAX_POINTS; + keyboard_finger_id[id] = MAX_POINTS; + udp_finger_id[id] = MAX_POINTS; } } @@ -142,6 +143,10 @@ std::optional Controller_Gesture::GetUnusedFingerID() const { std::size_t Controller_Gesture::UpdateTouchInputEvent( const std::tuple& touch_input, std::size_t finger_id) { const auto& [x, y, pressed] = touch_input; + if (finger_id > MAX_POINTS) { + LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id); + return MAX_POINTS; + } if (pressed) { if (finger_id == MAX_POINTS) { const auto first_free_id = GetUnusedFingerID(); diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index 5219f2dad..be60492a4 100755 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp @@ -5,6 +5,7 @@ #include #include #include "common/common_types.h" +#include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" @@ -118,6 +119,10 @@ std::optional Controller_Touchscreen::GetUnusedFingerID() const { std::size_t Controller_Touchscreen::UpdateTouchInputEvent( const std::tuple& touch_input, std::size_t finger_id) { const auto& [x, y, pressed] = touch_input; + if (finger_id > MAX_FINGERS) { + LOG_ERROR(Service_HID, "Invalid finger id {}", finger_id); + return MAX_FINGERS; + } if (pressed) { Attributes attribute{}; if (finger_id == MAX_FINGERS) { diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp index f9089bf2f..2f0edde2a 100755 --- 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 100755 --- 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 100755 --- 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 100755 --- 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; };