| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- using MsgPack;
- using MsgPack.Serialization;
- using Ryujinx.Common.Logging;
- using Ryujinx.Common.Utilities;
- using Ryujinx.Horizon.Common;
- using Ryujinx.Horizon.Prepo.Types;
- using Ryujinx.Horizon.Sdk.Account;
- using Ryujinx.Horizon.Sdk.Prepo;
- using Ryujinx.Horizon.Sdk.Sf;
- using Ryujinx.Horizon.Sdk.Sf.Hipc;
- using System;
- using System.Text;
- namespace Ryujinx.Horizon.Prepo.Ipc
- {
- partial class PrepoService : IPrepoService
- {
- enum PlayReportKind
- {
- Normal,
- System
- }
- private readonly PrepoServicePermissionLevel _permissionLevel;
- private ulong _systemSessionId;
- private bool _immediateTransmissionEnabled = false;
- private bool _userAgreementCheckEnabled = true;
- public PrepoService(PrepoServicePermissionLevel permissionLevel)
- {
- _permissionLevel = permissionLevel;
- }
- [CmifCommand(10100)] // 1.0.0-5.1.0
- [CmifCommand(10102)] // 6.0.0-9.2.0
- [CmifCommand(10104)] // 10.0.0+
- public Result SaveReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
- {
- if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
- {
- return PrepoResult.PermissionDenied;
- }
- ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, Uid.Null);
- return Result.Success;
- }
- [CmifCommand(10101)] // 1.0.0-5.1.0
- [CmifCommand(10103)] // 6.0.0-9.2.0
- [CmifCommand(10105)] // 10.0.0+
- public Result SaveReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
- {
- if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
- {
- return PrepoResult.PermissionDenied;
- }
- ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, userId, true);
- return Result.Success;
- }
- [CmifCommand(10200)]
- public Result RequestImmediateTransmission()
- {
- _immediateTransmissionEnabled = true;
- // It signals an event of nn::prepo::detail::service::core::TransmissionStatusManager that requests the transmission of the report.
- // Since we don't use reports, it's fine to do nothing.
- return Result.Success;
- }
- [CmifCommand(10300)]
- public Result GetTransmissionStatus(out int status)
- {
- status = 0;
- if (_immediateTransmissionEnabled && _userAgreementCheckEnabled)
- {
- status = 1;
- }
- return Result.Success;
- }
- [CmifCommand(10400)] // 9.0.0+
- public Result GetSystemSessionId(out ulong systemSessionId)
- {
- systemSessionId = default;
- if ((_permissionLevel & PrepoServicePermissionLevel.User) == 0)
- {
- return PrepoResult.PermissionDenied;
- }
- if (_systemSessionId == 0)
- {
- _systemSessionId = (ulong)Random.Shared.NextInt64();
- }
- systemSessionId = _systemSessionId;
- return Result.Success;
- }
- [CmifCommand(20100)]
- public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer)
- {
- if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
- {
- return PrepoResult.PermissionDenied;
- }
- return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, Uid.Null, false, applicationId);
- }
- [CmifCommand(20101)]
- public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer)
- {
- if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
- {
- return PrepoResult.PermissionDenied;
- }
- return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, userId, true, applicationId);
- }
- [CmifCommand(40100)] // 2.0.0+
- public Result IsUserAgreementCheckEnabled(out bool enabled)
- {
- enabled = false;
- if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System)
- {
- enabled = _userAgreementCheckEnabled;
- // If "enabled" is false, it sets some internal fields to 0.
- // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and returns the contained bool.
- // We can return the private bool instead, we don't care about the agreement since we don't send reports.
- return Result.Success;
- }
- return PrepoResult.PermissionDenied;
- }
- [CmifCommand(40101)] // 2.0.0+
- public Result SetUserAgreementCheckEnabled(bool enabled)
- {
- if (_permissionLevel == PrepoServicePermissionLevel.User || _permissionLevel == PrepoServicePermissionLevel.System)
- {
- _userAgreementCheckEnabled = enabled;
- // If "enabled" is false, it sets some internal fields to 0.
- // Then, it mounts "prepo-sys:/is_user_agreement_check_enabled.bin" and stores the "enabled" value.
- // We can store in the private bool instead, we don't care about the agreement since we don't send reports.
- return Result.Success;
- }
- return PrepoResult.PermissionDenied;
- }
- private static Result ProcessPlayReport(PlayReportKind playReportKind, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid, Uid userId, bool withUserId = false, Sdk.Ncm.ApplicationId applicationId = default)
- {
- if (withUserId)
- {
- if (userId.IsNull)
- {
- return PrepoResult.InvalidArgument;
- }
- }
- if (gameRoomBuffer.Length > 31)
- {
- return PrepoResult.InvalidArgument;
- }
- string gameRoom = Encoding.UTF8.GetString(gameRoomBuffer).TrimEnd();
- if (gameRoom == string.Empty)
- {
- return PrepoResult.InvalidState;
- }
- if (reportBuffer.Length == 0)
- {
- return PrepoResult.InvalidBufferSize;
- }
- StringBuilder builder = new();
- MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
- builder.AppendLine();
- builder.AppendLine("PlayReport log:");
- builder.AppendLine($" Kind: {playReportKind}");
- // NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned.
- // Reports are stored internally and an event is signaled to transmit them.
- if (pid != 0)
- {
- builder.AppendLine($" Pid: {pid}");
- }
- else
- {
- builder.AppendLine($" ApplicationId: {applicationId}");
- }
- if (!userId.IsNull)
- {
- builder.AppendLine($" UserId: {userId}");
- }
- builder.AppendLine($" Room: {gameRoom}");
- builder.AppendLine($" Report: {MessagePackObjectFormatter.Format(deserializedReport)}");
- Logger.Info?.Print(LogClass.ServicePrepo, builder.ToString());
- return Result.Success;
- }
- }
- }
|