/ src / Ryujinx.HLE / HOS / Services / Pctl / ParentalControlServiceFactory / IParentalControlService.cs
IParentalControlService.cs
  1  using Ryujinx.Common.Logging;
  2  using Ryujinx.HLE.HOS.Services.Arp;
  3  using System;
  4  using static LibHac.Ns.ApplicationControlProperty;
  5  
  6  namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
  7  {
  8      class IParentalControlService : IpcService
  9      {
 10          private readonly ulong _pid;
 11          private readonly int _permissionFlag;
 12          private ulong _titleId;
 13          private ParentalControlFlagValue _parentalControlFlag;
 14  #pragma warning disable IDE0052, CS0414 // Remove unread private member
 15          private int[] _ratingAge;
 16  
 17          // TODO: Find where they are set.
 18          private readonly bool _restrictionEnabled = false;
 19          private readonly bool _featuresRestriction = false;
 20          private bool _freeCommunicationEnabled = false;
 21          private readonly bool _stereoVisionRestrictionConfigurable = true;
 22          private bool _stereoVisionRestriction = false;
 23  #pragma warning restore IDE0052, CS0414
 24  
 25          public IParentalControlService(ServiceCtx context, ulong pid, bool withInitialize, int permissionFlag)
 26          {
 27              _pid = pid;
 28              _permissionFlag = permissionFlag;
 29  
 30              if (withInitialize)
 31              {
 32                  Initialize(context);
 33              }
 34          }
 35  
 36          [CommandCmif(1)] // 4.0.0+
 37          // Initialize()
 38          public ResultCode Initialize(ServiceCtx context)
 39          {
 40              if ((_permissionFlag & 0x8001) == 0)
 41              {
 42                  return ResultCode.PermissionDenied;
 43              }
 44  
 45              ResultCode resultCode = ResultCode.InvalidPid;
 46  
 47              if (_pid != 0)
 48              {
 49                  if ((_permissionFlag & 0x40) == 0)
 50                  {
 51                      ulong titleId = ApplicationLaunchProperty.GetByPid(context).TitleId;
 52  
 53                      if (titleId != 0)
 54                      {
 55                          _titleId = titleId;
 56  
 57                          // TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields.
 58                          _ratingAge = Array.ConvertAll(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.ItemsRo.ToArray(), Convert.ToInt32);
 59                          _parentalControlFlag = context.Device.Processes.ActiveApplication.ApplicationControlProperties.ParentalControlFlag;
 60                      }
 61                  }
 62  
 63                  if (_titleId != 0)
 64                  {
 65                      // TODO: Service store some private fields in another object.
 66  
 67                      if ((_permissionFlag & 0x8040) == 0)
 68                      {
 69                          // TODO: Service store TitleId and FreeCommunicationEnabled in another object.
 70                          //       When it's done it signal an event in this object.
 71                          Logger.Stub?.PrintStub(LogClass.ServicePctl);
 72                      }
 73                  }
 74  
 75                  resultCode = ResultCode.Success;
 76              }
 77  
 78              return resultCode;
 79          }
 80  
 81          [CommandCmif(1001)]
 82          // CheckFreeCommunicationPermission()
 83          public ResultCode CheckFreeCommunicationPermission(ServiceCtx context)
 84          {
 85              if (_parentalControlFlag == ParentalControlFlagValue.FreeCommunication && _restrictionEnabled)
 86              {
 87                  // TODO: It seems to checks if an entry exists in the FreeCommunicationApplicationList using the TitleId.
 88                  //       Then it returns FreeCommunicationDisabled if the entry doesn't exist.
 89  
 90                  return ResultCode.FreeCommunicationDisabled;
 91              }
 92  
 93              _freeCommunicationEnabled = true;
 94  
 95              Logger.Stub?.PrintStub(LogClass.ServicePctl);
 96  
 97              return ResultCode.Success;
 98          }
 99  
100          [CommandCmif(1017)] // 10.0.0+
101          // EndFreeCommunication()
102          public ResultCode EndFreeCommunication(ServiceCtx context)
103          {
104              _freeCommunicationEnabled = false;
105  
106              return ResultCode.Success;
107          }
108  
109          [CommandCmif(1013)] // 4.0.0+
110          // ConfirmStereoVisionPermission()
111          public ResultCode ConfirmStereoVisionPermission(ServiceCtx context)
112          {
113              return IsStereoVisionPermittedImpl();
114          }
115  
116          [CommandCmif(1018)]
117          // IsFreeCommunicationAvailable()
118          public ResultCode IsFreeCommunicationAvailable(ServiceCtx context)
119          {
120              if (_parentalControlFlag == ParentalControlFlagValue.FreeCommunication && _restrictionEnabled)
121              {
122                  // TODO: It seems to checks if an entry exists in the FreeCommunicationApplicationList using the TitleId.
123                  //       Then it returns FreeCommunicationDisabled if the entry doesn't exist.
124  
125                  return ResultCode.FreeCommunicationDisabled;
126              }
127  
128              Logger.Stub?.PrintStub(LogClass.ServicePctl);
129  
130              return ResultCode.Success;
131          }
132  
133          [CommandCmif(1031)]
134          // IsRestrictionEnabled() -> b8
135          public ResultCode IsRestrictionEnabled(ServiceCtx context)
136          {
137              if ((_permissionFlag & 0x140) == 0)
138              {
139                  return ResultCode.PermissionDenied;
140              }
141  
142              context.ResponseData.Write(_restrictionEnabled);
143  
144              return ResultCode.Success;
145          }
146  
147          [CommandCmif(1061)] // 4.0.0+
148          // ConfirmStereoVisionRestrictionConfigurable()
149          public ResultCode ConfirmStereoVisionRestrictionConfigurable(ServiceCtx context)
150          {
151              if ((_permissionFlag & 2) == 0)
152              {
153                  return ResultCode.PermissionDenied;
154              }
155  
156              if (_stereoVisionRestrictionConfigurable)
157              {
158                  return ResultCode.Success;
159              }
160              else
161              {
162                  return ResultCode.StereoVisionRestrictionConfigurableDisabled;
163              }
164          }
165  
166          [CommandCmif(1062)] // 4.0.0+
167          // GetStereoVisionRestriction() -> bool
168          public ResultCode GetStereoVisionRestriction(ServiceCtx context)
169          {
170              if ((_permissionFlag & 0x200) == 0)
171              {
172                  return ResultCode.PermissionDenied;
173              }
174  
175  #pragma warning disable // Remove unnecessary value assignment
176              bool stereoVisionRestriction = false;
177  #pragma warning restore IDE0059
178  
179              if (_stereoVisionRestrictionConfigurable)
180              {
181                  stereoVisionRestriction = _stereoVisionRestriction;
182              }
183  
184              context.ResponseData.Write(stereoVisionRestriction);
185  
186              return ResultCode.Success;
187          }
188  
189          [CommandCmif(1063)] // 4.0.0+
190          // SetStereoVisionRestriction(bool)
191          public ResultCode SetStereoVisionRestriction(ServiceCtx context)
192          {
193              if ((_permissionFlag & 0x200) == 0)
194              {
195                  return ResultCode.PermissionDenied;
196              }
197  
198              bool stereoVisionRestriction = context.RequestData.ReadBoolean();
199  
200              if (!_featuresRestriction)
201              {
202                  if (_stereoVisionRestrictionConfigurable)
203                  {
204                      _stereoVisionRestriction = stereoVisionRestriction;
205  
206                      // TODO: It signals an internal event of service. We have to determine where this event is used.
207                  }
208              }
209  
210              return ResultCode.Success;
211          }
212  
213          [CommandCmif(1064)] // 5.0.0+
214          // ResetConfirmedStereoVisionPermission()
215          public ResultCode ResetConfirmedStereoVisionPermission(ServiceCtx context)
216          {
217              return ResultCode.Success;
218          }
219  
220          [CommandCmif(1065)] // 5.0.0+
221          // IsStereoVisionPermitted() -> bool
222          public ResultCode IsStereoVisionPermitted(ServiceCtx context)
223          {
224              bool isStereoVisionPermitted = false;
225  
226              ResultCode resultCode = IsStereoVisionPermittedImpl();
227  
228              if (resultCode == ResultCode.Success)
229              {
230                  isStereoVisionPermitted = true;
231              }
232  
233              context.ResponseData.Write(isStereoVisionPermitted);
234  
235              return resultCode;
236          }
237  
238          private ResultCode IsStereoVisionPermittedImpl()
239          {
240              /*
241                  // TODO: Application Exemptions are read from file "appExemptions.dat" in the service savedata.
242                  //       Since we don't support the pctl savedata for now, this can be implemented later.
243  
244                  if (appExemption)
245                  {
246                      return ResultCode.Success;
247                  }
248              */
249  
250              if (_stereoVisionRestrictionConfigurable && _stereoVisionRestriction)
251              {
252                  return ResultCode.StereoVisionDenied;
253              }
254              else
255              {
256                  return ResultCode.Success;
257              }
258          }
259      }
260  }