summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/control_metadata.cpp8
-rw-r--r--src/core/file_sys/control_metadata.h2
-rw-r--r--src/core/hle/service/pctl/module.cpp247
-rw-r--r--src/core/hle/service/pctl/module.h19
-rw-r--r--src/core/hle/service/pctl/pctl.cpp5
-rw-r--r--src/core/hle/service/pctl/pctl.h3
6 files changed, 253 insertions, 31 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 {
100 return raw.device_save_data_size; 100 return raw.device_save_data_size;
101} 101}
102 102
103u32 NACP::GetParentalControlFlag() const {
104 return raw.parental_control;
105}
106
107const std::array<u8, 0x20>& NACP::GetRatingAge() const {
108 return raw.rating_age;
109}
110
103std::vector<u8> NACP::GetRawBytes() const { 111std::vector<u8> NACP::GetRawBytes() const {
104 std::vector<u8> out(sizeof(RawNACP)); 112 std::vector<u8> out(sizeof(RawNACP));
105 std::memcpy(out.data(), &raw, sizeof(RawNACP)); 113 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:
114 std::vector<u8> GetRawBytes() const; 114 std::vector<u8> GetRawBytes() const;
115 bool GetUserAccountSwitchLock() const; 115 bool GetUserAccountSwitchLock() const;
116 u64 GetDeviceSaveDataSize() const; 116 u64 GetDeviceSaveDataSize() const;
117 u32 GetParentalControlFlag() const;
118 const std::array<u8, 0x20>& GetRatingAge() const;
117 119
118private: 120private:
119 RawNACP raw{}; 121 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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/file_sys/control_metadata.h"
8#include "core/file_sys/patch_manager.h"
6#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/process.h"
7#include "core/hle/service/pctl/module.h" 11#include "core/hle/service/pctl/module.h"
8#include "core/hle/service/pctl/pctl.h" 12#include "core/hle/service/pctl/pctl.h"
9 13
10namespace Service::PCTL { 14namespace Service::PCTL {
11 15
16namespace Error {
17
18constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101};
19constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
20constexpr ResultCode ResultNoCapatability{ErrorModule::PCTL, 131};
21constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
22
23} // namespace Error
24
12class IParentalControlService final : public ServiceFramework<IParentalControlService> { 25class IParentalControlService final : public ServiceFramework<IParentalControlService> {
13public: 26public:
14 explicit IParentalControlService(Core::System& system_) 27 explicit IParentalControlService(Core::System& system_, Capability capability)
15 : ServiceFramework{system_, "IParentalControlService"} { 28 : ServiceFramework{system_, "IParentalControlService"}, system(system_),
29 capability(capability) {
16 // clang-format off 30 // clang-format off
17 static const FunctionInfo functions[] = { 31 static const FunctionInfo functions[] = {
18 {1, &IParentalControlService::Initialize, "Initialize"}, 32 {1, &IParentalControlService::Initialize, "Initialize"},
@@ -28,13 +42,13 @@ public:
28 {1010, nullptr, "IsRestrictedSystemSettingsEntered"}, 42 {1010, nullptr, "IsRestrictedSystemSettingsEntered"},
29 {1011, nullptr, "RevertRestrictedSystemSettingsEntered"}, 43 {1011, nullptr, "RevertRestrictedSystemSettingsEntered"},
30 {1012, nullptr, "GetRestrictedFeatures"}, 44 {1012, nullptr, "GetRestrictedFeatures"},
31 {1013, nullptr, "ConfirmStereoVisionPermission"}, 45 {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"},
32 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"}, 46 {1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
33 {1015, nullptr, "ConfirmPlayableApplicationVideo"}, 47 {1015, nullptr, "ConfirmPlayableApplicationVideo"},
34 {1016, nullptr, "ConfirmShowNewsPermission"}, 48 {1016, nullptr, "ConfirmShowNewsPermission"},
35 {1017, nullptr, "EndFreeCommunication"}, 49 {1017, nullptr, "EndFreeCommunication"},
36 {1018, nullptr, "IsFreeCommunicationAvailable"}, 50 {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"},
37 {1031, nullptr, "IsRestrictionEnabled"}, 51 {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"},
38 {1032, nullptr, "GetSafetyLevel"}, 52 {1032, nullptr, "GetSafetyLevel"},
39 {1033, nullptr, "SetSafetyLevel"}, 53 {1033, nullptr, "SetSafetyLevel"},
40 {1034, nullptr, "GetSafetyLevelSettings"}, 54 {1034, nullptr, "GetSafetyLevelSettings"},
@@ -119,62 +133,234 @@ public:
119 } 133 }
120 134
121private: 135private:
136 bool CheckFreeCommunicationPermissionImpl() {
137 if (states.temporary_unlocked) {
138 return true;
139 }
140 if ((states.application_info.parental_control_flag & 1) == 0) {
141 return true;
142 }
143 if (pin_code[0] == '\0') {
144 return true;
145 }
146 if (!settings.is_free_communication_default_on) {
147 return true;
148 }
149 // TODO(ogniK): Check for blacklisted/exempted applications
150 return true;
151 }
152
153 bool ConfirmStereoVisionPermissionImpl() {
154 if (states.temporary_unlocked) {
155 return true;
156 }
157 if (pin_code[0] == '\0') {
158 return true;
159 }
160 if (!settings.is_stero_vision_restricted) {
161 return false;
162 }
163 return true;
164 }
165
166 void SetStereoVisionRestrictionImpl(bool is_restricted) {
167 if (settings.disabled) {
168 return;
169 }
170
171 if (pin_code[0] == '\0') {
172 return;
173 }
174 settings.is_stero_vision_restricted = is_restricted;
175 }
176
122 void Initialize(Kernel::HLERequestContext& ctx) { 177 void Initialize(Kernel::HLERequestContext& ctx) {
123 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 178 LOG_DEBUG(Service_PCTL, "called");
179 IPC::ResponseBuilder rb{ctx, 2};
180
181 if (False(capability & (Capability::Application | Capability::System))) {
182 LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}",
183 static_cast<s32>(capability));
184 return;
185 }
186
187 // TODO(ogniK): Recovery
188
189 const auto tid = system.CurrentProcess()->GetTitleID();
190 if (tid != 0) {
191 const FileSys::PatchManager pm{tid, system.GetFileSystemController(),
192 system.GetContentProvider()};
193 const auto control = pm.GetControlMetadata();
194 if (control.first) {
195 states.tid_from_event = 0;
196 states.launch_time_valid = false;
197 states.is_suspended = false;
198 states.free_communication = false;
199 states.stereo_vision = false;
200 states.application_info = ApplicationInfo{
201 .tid = tid,
202 .age_rating = control.first->GetRatingAge(),
203 .parental_control_flag = control.first->GetParentalControlFlag(),
204 .capability = capability,
205 };
206
207 if (False(capability & (Capability::System | Capability::Recovery))) {
208 // TODO(ogniK): Signal application launch event
209 }
210 }
211 }
124 212
125 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
126 rb.Push(RESULT_SUCCESS); 213 rb.Push(RESULT_SUCCESS);
127 } 214 }
128 215
129 void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { 216 void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) {
130 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 217 LOG_DEBUG(Service_PCTL, "called");
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 if (!CheckFreeCommunicationPermissionImpl()) {
221 rb.Push(Error::ResultNoFreeCommunication);
222 } else {
223 rb.Push(RESULT_SUCCESS);
224 }
225
226 states.free_communication = true;
227 }
228
229 void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) {
230 LOG_DEBUG(Service_PCTL, "called");
231 states.stereo_vision = true;
131 232
132 IPC::ResponseBuilder rb{ctx, 2}; 233 IPC::ResponseBuilder rb{ctx, 2};
133 rb.Push(RESULT_SUCCESS); 234 rb.Push(RESULT_SUCCESS);
134 } 235 }
135 236
136 void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { 237 void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) {
137 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 238 LOG_WARNING(Service_PCTL, "(STUBBED) called");
138 239
139 IPC::ResponseBuilder rb{ctx, 2}; 240 IPC::ResponseBuilder rb{ctx, 2};
241 if (!CheckFreeCommunicationPermissionImpl()) {
242 rb.Push(Error::ResultNoFreeCommunication);
243 } else {
244 rb.Push(RESULT_SUCCESS);
245 }
246 }
247
248 void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) {
249 LOG_DEBUG(Service_PCTL, "called");
250
251 IPC::ResponseBuilder rb{ctx, 3};
252 if (False(capability & (Capability::Status | Capability::Recovery))) {
253 LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!");
254 rb.Push(Error::ResultNoCapatability);
255 rb.Push(false);
256 return;
257 }
258
259 rb.Push(pin_code[0] != '\0');
260 }
261
262 void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
263 LOG_DEBUG(Service_PCTL, "called");
264
265 IPC::ResponseBuilder rb{ctx, 2};
266
267 if (False(capability & Capability::SteroVision)) {
268 LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!");
269 rb.Push(Error::ResultNoCapatability);
270 return;
271 }
272
273 if (pin_code[0] == '\0') {
274 rb.Push(Error::ResultNoRestrictionEnabled);
275 return;
276 }
277
140 rb.Push(RESULT_SUCCESS); 278 rb.Push(RESULT_SUCCESS);
141 } 279 }
142 280
143 void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { 281 void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
144 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 282 LOG_DEBUG(Service_PCTL, "called");
145 283
146 IPC::ResponseBuilder rb{ctx, 3}; 284 IPC::ResponseBuilder rb{ctx, 3};
147 rb.Push(RESULT_SUCCESS); 285 if (!ConfirmStereoVisionPermissionImpl()) {
148 rb.Push(true); 286 rb.Push(Error::ResultStereoVisionRestricted);
287 rb.Push(false);
288 } else {
289 rb.Push(RESULT_SUCCESS);
290 rb.Push(true);
291 }
149 } 292 }
150 293
151 void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { 294 void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
152 IPC::RequestParser rp{ctx}; 295 IPC::RequestParser rp{ctx};
153 const auto can_use = rp.Pop<bool>(); 296 const auto can_use = rp.Pop<bool>();
154 LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use); 297 LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use);
155
156 can_use_stereo_vision = can_use;
157 298
158 IPC::ResponseBuilder rb{ctx, 2}; 299 IPC::ResponseBuilder rb{ctx, 2};
300 if (False(capability & Capability::SteroVision)) {
301 LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!");
302 rb.Push(Error::ResultNoCapatability);
303 return;
304 }
305
306 SetStereoVisionRestrictionImpl(can_use);
159 rb.Push(RESULT_SUCCESS); 307 rb.Push(RESULT_SUCCESS);
160 } 308 }
161 309
162 void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { 310 void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
163 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 311 LOG_DEBUG(Service_PCTL, "called");
164 312
165 IPC::ResponseBuilder rb{ctx, 3}; 313 IPC::ResponseBuilder rb{ctx, 3};
314 if (False(capability & Capability::SteroVision)) {
315 LOG_ERROR(Service_PCTL, "Application does not have SteroVision capability!");
316 rb.Push(Error::ResultNoCapatability);
317 rb.Push(false);
318 return;
319 }
320
166 rb.Push(RESULT_SUCCESS); 321 rb.Push(RESULT_SUCCESS);
167 rb.Push(can_use_stereo_vision); 322 rb.Push(settings.is_stero_vision_restricted);
168 } 323 }
169 324
170 void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { 325 void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
171 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 326 LOG_DEBUG(Service_PCTL, "called");
327
328 states.stereo_vision = false;
172 329
173 IPC::ResponseBuilder rb{ctx, 2}; 330 IPC::ResponseBuilder rb{ctx, 2};
174 rb.Push(RESULT_SUCCESS); 331 rb.Push(RESULT_SUCCESS);
175 } 332 }
176 333
334 struct ApplicationInfo {
335 u64 tid{};
336 std::array<u8, 32> age_rating{};
337 u32 parental_control_flag{};
338 Capability capability{};
339 };
340
341 struct States {
342 u64 current_tid{};
343 ApplicationInfo application_info{};
344 u64 tid_from_event{};
345 bool launch_time_valid{};
346 bool is_suspended{};
347 bool temporary_unlocked{};
348 bool free_communication{};
349 bool stereo_vision{};
350 };
351
352 struct ParentalControlSettings {
353 bool is_stero_vision_restricted{};
354 bool is_free_communication_default_on{};
355 bool disabled{};
356 };
357
358 States states{};
359 ParentalControlSettings settings{};
360 std::array<char, 8> pin_code{};
177 bool can_use_stereo_vision = true; 361 bool can_use_stereo_vision = true;
362 Core::System& system;
363 Capability capability{};
178}; 364};
179 365
180void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { 366void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
@@ -182,7 +368,9 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
182 368
183 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 369 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
184 rb.Push(RESULT_SUCCESS); 370 rb.Push(RESULT_SUCCESS);
185 rb.PushIpcInterface<IParentalControlService>(system); 371 // TODO(ogniK): Get TID from process
372
373 rb.PushIpcInterface<IParentalControlService>(system, capability);
186} 374}
187 375
188void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { 376void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) {
@@ -190,21 +378,28 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
190 378
191 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 379 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
192 rb.Push(RESULT_SUCCESS); 380 rb.Push(RESULT_SUCCESS);
193 rb.PushIpcInterface<IParentalControlService>(system); 381 rb.PushIpcInterface<IParentalControlService>(system, capability);
194} 382}
195 383
196Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, 384Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
197 const char* name) 385 const char* name, Capability capability)
198 : ServiceFramework{system_, name}, module{std::move(module_)} {} 386 : ServiceFramework{system_, name}, module{std::move(module_)}, capability(capability) {}
199 387
200Module::Interface::~Interface() = default; 388Module::Interface::~Interface() = default;
201 389
202void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 390void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
203 auto module = std::make_shared<Module>(); 391 auto module = std::make_shared<Module>();
204 std::make_shared<PCTL>(system, module, "pctl")->InstallAsService(service_manager); 392 std::make_shared<PCTL>(system, module, "pctl",
205 std::make_shared<PCTL>(system, module, "pctl:a")->InstallAsService(service_manager); 393 Capability::Application | Capability::SnsPost | Capability::Status |
206 std::make_shared<PCTL>(system, module, "pctl:r")->InstallAsService(service_manager); 394 Capability::SteroVision)
207 std::make_shared<PCTL>(system, module, "pctl:s")->InstallAsService(service_manager); 395 ->InstallAsService(service_manager);
396 // TODO(ogniK): Implement remaining capabilities
397 std::make_shared<PCTL>(system, module, "pctl:a", Capability::None)
398 ->InstallAsService(service_manager);
399 std::make_shared<PCTL>(system, module, "pctl:r", Capability::None)
400 ->InstallAsService(service_manager);
401 std::make_shared<PCTL>(system, module, "pctl:s", Capability::None)
402 ->InstallAsService(service_manager);
208} 403}
209 404
210} // namespace Service::PCTL 405} // 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 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/common_funcs.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Core { 10namespace Core {
@@ -12,12 +13,23 @@ class System;
12 13
13namespace Service::PCTL { 14namespace Service::PCTL {
14 15
16enum class Capability : s32 {
17 None = 0x0,
18 Application = 1 << 0,
19 SnsPost = 1 << 1,
20 Recovery = 1 << 6,
21 Status = 1 << 8,
22 SteroVision = 1 << 9,
23 System = 1 << 15,
24};
25DECLARE_ENUM_FLAG_OPERATORS(Capability);
26
15class Module final { 27class Module final {
16public: 28public:
17 class Interface : public ServiceFramework<Interface> { 29 class Interface : public ServiceFramework<Interface> {
18 public: 30 public:
19 explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, 31 explicit Interface(Core::System& system_, std::shared_ptr<Module> module_, const char* name,
20 const char* name); 32 Capability capability);
21 ~Interface() override; 33 ~Interface() override;
22 34
23 void CreateService(Kernel::HLERequestContext& ctx); 35 void CreateService(Kernel::HLERequestContext& ctx);
@@ -25,6 +37,9 @@ public:
25 37
26 protected: 38 protected:
27 std::shared_ptr<Module> module; 39 std::shared_ptr<Module> module;
40
41 private:
42 Capability capability{};
28 }; 43 };
29}; 44};
30 45
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 @@
6 6
7namespace Service::PCTL { 7namespace Service::PCTL {
8 8
9PCTL::PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name) 9PCTL::PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name,
10 : Interface{system_, std::move(module_), name} { 10 Capability capability)
11 : Interface{system_, std::move(module_), name, capability} {
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &PCTL::CreateService, "CreateService"}, 13 {0, &PCTL::CreateService, "CreateService"},
13 {1, &PCTL::CreateServiceWithoutInitialize, "CreateServiceWithoutInitialize"}, 14 {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 {
14 14
15class PCTL final : public Module::Interface { 15class PCTL final : public Module::Interface {
16public: 16public:
17 explicit PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name); 17 explicit PCTL(Core::System& system_, std::shared_ptr<Module> module_, const char* name,
18 Capability capability);
18 ~PCTL() override; 19 ~PCTL() override;
19}; 20};
20 21