summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.cpp248
-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, 254 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 e6cd4b3c7..dc59702f1 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 ResultNoCapability{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"},
@@ -122,62 +136,235 @@ public:
122 } 136 }
123 137
124private: 138private:
139 bool CheckFreeCommunicationPermissionImpl() const {
140 if (states.temporary_unlocked) {
141 return true;
142 }
143 if ((states.application_info.parental_control_flag & 1) == 0) {
144 return true;
145 }
146 if (pin_code[0] == '\0') {
147 return true;
148 }
149 if (!settings.is_free_communication_default_on) {
150 return true;
151 }
152 // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here
153 // but as we don't have multiproceses support yet, we can just assume our application is
154 // valid for the time being
155 return true;
156 }
157
158 bool ConfirmStereoVisionPermissionImpl() const {
159 if (states.temporary_unlocked) {
160 return true;
161 }
162 if (pin_code[0] == '\0') {
163 return true;
164 }
165 if (!settings.is_stero_vision_restricted) {
166 return false;
167 }
168 return true;
169 }
170
171 void SetStereoVisionRestrictionImpl(bool is_restricted) {
172 if (settings.disabled) {
173 return;
174 }
175
176 if (pin_code[0] == '\0') {
177 return;
178 }
179 settings.is_stero_vision_restricted = is_restricted;
180 }
181
125 void Initialize(Kernel::HLERequestContext& ctx) { 182 void Initialize(Kernel::HLERequestContext& ctx) {
126 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 183 LOG_DEBUG(Service_PCTL, "called");
184 IPC::ResponseBuilder rb{ctx, 2};
185
186 if (False(capability & (Capability::Application | Capability::System))) {
187 LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", capability);
188 return;
189 }
190
191 // TODO(ogniK): Recovery flag initialization for pctl:r
192
193 const auto tid = system.CurrentProcess()->GetTitleID();
194 if (tid != 0) {
195 const FileSys::PatchManager pm{tid, system.GetFileSystemController(),
196 system.GetContentProvider()};
197 const auto control = pm.GetControlMetadata();
198 if (control.first) {
199 states.tid_from_event = 0;
200 states.launch_time_valid = false;
201 states.is_suspended = false;
202 states.free_communication = false;
203 states.stereo_vision = false;
204 states.application_info = ApplicationInfo{
205 .tid = tid,
206 .age_rating = control.first->GetRatingAge(),
207 .parental_control_flag = control.first->GetParentalControlFlag(),
208 .capability = capability,
209 };
210
211 if (False(capability & (Capability::System | Capability::Recovery))) {
212 // TODO(ogniK): Signal application launch event
213 }
214 }
215 }
127 216
128 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
129 rb.Push(RESULT_SUCCESS); 217 rb.Push(RESULT_SUCCESS);
130 } 218 }
131 219
132 void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) { 220 void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) {
133 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 221 LOG_DEBUG(Service_PCTL, "called");
222
223 IPC::ResponseBuilder rb{ctx, 2};
224 if (!CheckFreeCommunicationPermissionImpl()) {
225 rb.Push(Error::ResultNoFreeCommunication);
226 } else {
227 rb.Push(RESULT_SUCCESS);
228 }
229
230 states.free_communication = true;
231 }
232
233 void ConfirmStereoVisionPermission(Kernel::HLERequestContext& ctx) {
234 LOG_DEBUG(Service_PCTL, "called");
235 states.stereo_vision = true;
134 236
135 IPC::ResponseBuilder rb{ctx, 2}; 237 IPC::ResponseBuilder rb{ctx, 2};
136 rb.Push(RESULT_SUCCESS); 238 rb.Push(RESULT_SUCCESS);
137 } 239 }
138 240
139 void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) { 241 void IsFreeCommunicationAvailable(Kernel::HLERequestContext& ctx) {
140 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 242 LOG_WARNING(Service_PCTL, "(STUBBED) called");
141 243
142 IPC::ResponseBuilder rb{ctx, 2}; 244 IPC::ResponseBuilder rb{ctx, 2};
245 if (!CheckFreeCommunicationPermissionImpl()) {
246 rb.Push(Error::ResultNoFreeCommunication);
247 } else {
248 rb.Push(RESULT_SUCCESS);
249 }
250 }
251
252 void IsRestrictionEnabled(Kernel::HLERequestContext& ctx) {
253 LOG_DEBUG(Service_PCTL, "called");
254
255 IPC::ResponseBuilder rb{ctx, 3};
256 if (False(capability & (Capability::Status | Capability::Recovery))) {
257 LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!");
258 rb.Push(Error::ResultNoCapability);
259 rb.Push(false);
260 return;
261 }
262
263 rb.Push(pin_code[0] != '\0');
264 }
265
266 void ConfirmStereoVisionRestrictionConfigurable(Kernel::HLERequestContext& ctx) {
267 LOG_DEBUG(Service_PCTL, "called");
268
269 IPC::ResponseBuilder rb{ctx, 2};
270
271 if (False(capability & Capability::StereoVision)) {
272 LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
273 rb.Push(Error::ResultNoCapability);
274 return;
275 }
276
277 if (pin_code[0] == '\0') {
278 rb.Push(Error::ResultNoRestrictionEnabled);
279 return;
280 }
281
143 rb.Push(RESULT_SUCCESS); 282 rb.Push(RESULT_SUCCESS);
144 } 283 }
145 284
146 void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) { 285 void IsStereoVisionPermitted(Kernel::HLERequestContext& ctx) {
147 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 286 LOG_DEBUG(Service_PCTL, "called");
148 287
149 IPC::ResponseBuilder rb{ctx, 3}; 288 IPC::ResponseBuilder rb{ctx, 3};
150 rb.Push(RESULT_SUCCESS); 289 if (!ConfirmStereoVisionPermissionImpl()) {
151 rb.Push(true); 290 rb.Push(Error::ResultStereoVisionRestricted);
291 rb.Push(false);
292 } else {
293 rb.Push(RESULT_SUCCESS);
294 rb.Push(true);
295 }
152 } 296 }
153 297
154 void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { 298 void SetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
155 IPC::RequestParser rp{ctx}; 299 IPC::RequestParser rp{ctx};
156 const auto can_use = rp.Pop<bool>(); 300 const auto can_use = rp.Pop<bool>();
157 LOG_WARNING(Service_PCTL, "(STUBBED) called, can_use={}", can_use); 301 LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use);
158
159 can_use_stereo_vision = can_use;
160 302
161 IPC::ResponseBuilder rb{ctx, 2}; 303 IPC::ResponseBuilder rb{ctx, 2};
304 if (False(capability & Capability::StereoVision)) {
305 LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
306 rb.Push(Error::ResultNoCapability);
307 return;
308 }
309
310 SetStereoVisionRestrictionImpl(can_use);
162 rb.Push(RESULT_SUCCESS); 311 rb.Push(RESULT_SUCCESS);
163 } 312 }
164 313
165 void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) { 314 void GetStereoVisionRestriction(Kernel::HLERequestContext& ctx) {
166 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 315 LOG_DEBUG(Service_PCTL, "called");
167 316
168 IPC::ResponseBuilder rb{ctx, 3}; 317 IPC::ResponseBuilder rb{ctx, 3};
318 if (False(capability & Capability::StereoVision)) {
319 LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
320 rb.Push(Error::ResultNoCapability);
321 rb.Push(false);
322 return;
323 }
324
169 rb.Push(RESULT_SUCCESS); 325 rb.Push(RESULT_SUCCESS);
170 rb.Push(can_use_stereo_vision); 326 rb.Push(settings.is_stero_vision_restricted);
171 } 327 }
172 328
173 void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) { 329 void ResetConfirmedStereoVisionPermission(Kernel::HLERequestContext& ctx) {
174 LOG_WARNING(Service_PCTL, "(STUBBED) called"); 330 LOG_DEBUG(Service_PCTL, "called");
331
332 states.stereo_vision = false;
175 333
176 IPC::ResponseBuilder rb{ctx, 2}; 334 IPC::ResponseBuilder rb{ctx, 2};
177 rb.Push(RESULT_SUCCESS); 335 rb.Push(RESULT_SUCCESS);
178 } 336 }
179 337
338 struct ApplicationInfo {
339 u64 tid{};
340 std::array<u8, 32> age_rating{};
341 u32 parental_control_flag{};
342 Capability capability{};
343 };
344
345 struct States {
346 u64 current_tid{};
347 ApplicationInfo application_info{};
348 u64 tid_from_event{};
349 bool launch_time_valid{};
350 bool is_suspended{};
351 bool temporary_unlocked{};
352 bool free_communication{};
353 bool stereo_vision{};
354 };
355
356 struct ParentalControlSettings {
357 bool is_stero_vision_restricted{};
358 bool is_free_communication_default_on{};
359 bool disabled{};
360 };
361
362 States states{};
363 ParentalControlSettings settings{};
364 std::array<char, 8> pin_code{};
180 bool can_use_stereo_vision = true; 365 bool can_use_stereo_vision = true;
366 Core::System& system;
367 Capability capability{};
181}; 368};
182 369
183void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) { 370void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
@@ -185,7 +372,9 @@ void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
185 372
186 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 373 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
187 rb.Push(RESULT_SUCCESS); 374 rb.Push(RESULT_SUCCESS);
188 rb.PushIpcInterface<IParentalControlService>(system); 375 // TODO(ogniK): Get TID from process
376
377 rb.PushIpcInterface<IParentalControlService>(system, capability);
189} 378}
190 379
191void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) { 380void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) {
@@ -193,21 +382,28 @@ void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext
193 382
194 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 383 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
195 rb.Push(RESULT_SUCCESS); 384 rb.Push(RESULT_SUCCESS);
196 rb.PushIpcInterface<IParentalControlService>(system); 385 rb.PushIpcInterface<IParentalControlService>(system, capability);
197} 386}
198 387
199Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_, 388Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
200 const char* name) 389 const char* name, Capability capability)
201 : ServiceFramework{system_, name}, module{std::move(module_)} {} 390 : ServiceFramework{system_, name}, module{std::move(module_)}, capability(capability) {}
202 391
203Module::Interface::~Interface() = default; 392Module::Interface::~Interface() = default;
204 393
205void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 394void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
206 auto module = std::make_shared<Module>(); 395 auto module = std::make_shared<Module>();
207 std::make_shared<PCTL>(system, module, "pctl")->InstallAsService(service_manager); 396 std::make_shared<PCTL>(system, module, "pctl",
208 std::make_shared<PCTL>(system, module, "pctl:a")->InstallAsService(service_manager); 397 Capability::Application | Capability::SnsPost | Capability::Status |
209 std::make_shared<PCTL>(system, module, "pctl:r")->InstallAsService(service_manager); 398 Capability::StereoVision)
210 std::make_shared<PCTL>(system, module, "pctl:s")->InstallAsService(service_manager); 399 ->InstallAsService(service_manager);
400 // TODO(ogniK): Implement remaining capabilities
401 std::make_shared<PCTL>(system, module, "pctl:a", Capability::None)
402 ->InstallAsService(service_manager);
403 std::make_shared<PCTL>(system, module, "pctl:r", Capability::None)
404 ->InstallAsService(service_manager);
405 std::make_shared<PCTL>(system, module, "pctl:s", Capability::None)
406 ->InstallAsService(service_manager);
211} 407}
212 408
213} // namespace Service::PCTL 409} // namespace Service::PCTL
diff --git a/src/core/hle/service/pctl/module.h b/src/core/hle/service/pctl/module.h
index 4c7e09a3b..032481b00 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 : u32 {
17 None = 0,
18 Application = 1 << 0,
19 SnsPost = 1 << 1,
20 Recovery = 1 << 6,
21 Status = 1 << 8,
22 StereoVision = 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