From 4d2c8e2a442d2859a75c6c9162a192a0a9a221be Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Fri, 13 Jan 2023 01:50:14 +0100
Subject: [PATCH] Prepo: Fix SaveSystemReport* IPC definitions (#4278)

* Prepo: Fix SaveSystemReport IPC definitions

* Follow original code

* Fix args index in HipcGenerator

* Addresses feedback

* oops
---
 .../Hipc/HipcGenerator.cs                     |  6 ++-
 Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs     | 29 +++++++----
 Ryujinx.Horizon/Sdk/Account/Uid.cs            |  2 +-
 Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs      | 52 +++++++++++++++++++
 Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs    |  4 +-
 Ryujinx.Horizon/Sdk/Sm/ServiceName.cs         |  2 +-
 6 files changed, 78 insertions(+), 17 deletions(-)
 create mode 100644 Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs

diff --git a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
index 332e04d1..8f4c37f7 100644
--- a/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
+++ b/Ryujinx.Horizon.Generators/Hipc/HipcGenerator.cs
@@ -267,6 +267,8 @@ namespace Ryujinx.Horizon.Generators.Hipc
             }
 
             int index = 0;
+            int inArgIndex = 0;
+            int outArgIndex = 0;
             int inCopyHandleIndex = 0;
             int inMoveHandleIndex = 0;
             int inObjectIndex = 0;
@@ -284,7 +286,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
                 {
                     if (IsNonSpanOutBuffer(compilation, parameter))
                     {
-                        generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));");
+                        generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({outArgIndex++}));");
 
                         argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}";
                     }
@@ -302,7 +304,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
                     switch (argType)
                     {
                         case CommandArgType.InArgument:
-                            value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({index}))";
+                            value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({inArgIndex++}))";
                             break;
                         case CommandArgType.InCopyHandle:
                             value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})";
diff --git a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
index 672bba4e..e157fa56 100644
--- a/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
+++ b/Ryujinx.Horizon/Prepo/Ipc/PrepoService.cs
@@ -42,7 +42,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
                 return PrepoResult.PermissionDenied;
             }
 
-            ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null);
+            ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, Uid.Null);
 
             return Result.Success;
         }
@@ -57,7 +57,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
                 return PrepoResult.PermissionDenied;
             }
 
-            ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true);
+            ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, userId, true);
 
             return Result.Success;
         }
@@ -107,25 +107,25 @@ namespace Ryujinx.Horizon.Prepo.Ipc
         }
 
         [CmifCommand(20100)]
-        public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        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, pid, gameRoomBuffer, reportBuffer, Uid.Null);
+            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, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid)
+        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, pid, gameRoomBuffer, reportBuffer, userId, true);
+            return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, userId, true, applicationId);
         }
 
         [CmifCommand(40100)] // 2.0.0+
@@ -164,7 +164,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
             return PrepoResult.PermissionDenied;
         }
 
-        private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, Uid userId, bool withUserId = false)
+        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)
             {
@@ -191,16 +191,23 @@ namespace Ryujinx.Horizon.Prepo.Ipc
                 return PrepoResult.InvalidBufferSize;
             }
 
-            // 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.
-
             StringBuilder     builder            = new();
             MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
 
             builder.AppendLine();
             builder.AppendLine("PlayReport log:");
             builder.AppendLine($" Kind: {playReportKind}");
-            builder.AppendLine($" Pid: {pid}");
+
+            // 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)
             {
diff --git a/Ryujinx.Horizon/Sdk/Account/Uid.cs b/Ryujinx.Horizon/Sdk/Account/Uid.cs
index a0a39978..5aad0463 100644
--- a/Ryujinx.Horizon/Sdk/Account/Uid.cs
+++ b/Ryujinx.Horizon/Sdk/Account/Uid.cs
@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
 namespace Ryujinx.Horizon.Sdk.Account
 {
     [StructLayout(LayoutKind.Sequential)]
-    public readonly record struct Uid
+    readonly record struct Uid
     {
         public readonly long High;
         public readonly long Low;
diff --git a/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs b/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs
new file mode 100644
index 00000000..37b4cbfb
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/Ncm/ApplicationId.cs
@@ -0,0 +1,52 @@
+namespace Ryujinx.Horizon.Sdk.Ncm
+{
+    readonly struct ApplicationId
+    {
+        public readonly ulong Id;
+
+        public static int Length => sizeof(ulong);
+
+        public static ApplicationId First => new(0x0100000000010000);
+
+        public static ApplicationId Last => new(0x01FFFFFFFFFFFFFF);
+
+        public static ApplicationId Invalid => new(0);
+
+        public bool IsValid => Id >= First.Id && Id <= Last.Id;
+
+        public ApplicationId(ulong id)
+        {
+            Id = id;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return obj is ApplicationId applicationId && applicationId.Equals(this);
+        }
+
+        public bool Equals(ApplicationId other)
+        {
+            return other.Id == Id;
+        }
+
+        public override int GetHashCode()
+        {
+            return Id.GetHashCode();
+        }
+
+        public static bool operator ==(ApplicationId lhs, ApplicationId rhs)
+        {
+            return lhs.Equals(rhs);
+        }
+
+        public static bool operator !=(ApplicationId lhs, ApplicationId rhs)
+        {
+            return !lhs.Equals(rhs);
+        }
+
+        public override string ToString()
+        {
+            return $"0x{Id:x}";
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
index f61ee61b..042cb400 100644
--- a/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
+++ b/Ryujinx.Horizon/Sdk/Prepo/IPrepoService.cs
@@ -12,8 +12,8 @@ namespace Ryujinx.Horizon.Sdk.Prepo
         Result RequestImmediateTransmission();
         Result GetTransmissionStatus(out int status);
         Result GetSystemSessionId(out ulong systemSessionId);
-        Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
-        Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid);
+        Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer);
+        Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer);
         Result IsUserAgreementCheckEnabled(out bool enabled);
         Result SetUserAgreementCheckEnabled(bool enabled);
     }
diff --git a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
index dbb30078..9b7fae3f 100644
--- a/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
+++ b/Ryujinx.Horizon/Sdk/Sm/ServiceName.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
     {
         public static ServiceName Invalid { get; } = new ServiceName(0);
 
-        public bool IsInvalid => Packed == 0;
+        public bool IsValid => Packed != 0;
 
         public int Length => sizeof(ulong);