From a113ed081145c36fab77ce17c838f75e61f4ee7e Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Fri, 18 Feb 2022 02:00:06 +0100
Subject: [PATCH] Implement/Stub mnpp:app service and some hid calls (#3131)

* Implement/Stub mnpp:app service and some hid calls

This PR Implement/Stub the `mnpp:app` service (closes #3107) accordingly to RE, which seems to do some telemetry for China region only, so everything is stubbed.

This PR fixes some inconsistencies in the hid service too and stub EnableSixAxisSensorUnalteredPassthrough, IsSixAxisSensorUnalteredPassthroughEnabled, LoadSixAxisSensorCalibrationParameter, GetSixAxisSensorIcInformation calls (closes #3123 and closes #3124).

* Addresses Thog review
---
 Ryujinx.Common/Logging/LogClass.cs            |   1 +
 .../SystemAppletProxy/IWindowController.cs    |   4 +-
 Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs    | 143 +++++++++++++-----
 .../Services/Mnpp/IServiceForApplication.cs   |  63 ++++++++
 Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs   |  13 ++
 5 files changed, 186 insertions(+), 38 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs

diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs
index ad27f88f3a..a7d36765dc 100644
--- a/Ryujinx.Common/Logging/LogClass.cs
+++ b/Ryujinx.Common/Logging/LogClass.cs
@@ -39,6 +39,7 @@ namespace Ryujinx.Common.Logging
         ServiceLm,
         ServiceMii,
         ServiceMm,
+        ServiceMnpp,
         ServiceNfc,
         ServiceNfp,
         ServiceNgct,
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
index 7cb557ae00..37a3efcba6 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs
@@ -15,12 +15,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
         public ResultCode GetAppletResourceUserId(ServiceCtx context)
         {
-            Logger.Stub?.PrintStub(LogClass.ServiceAm);
-
             long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid);
 
             context.ResponseData.Write(appletResourceUserId);
 
+            Logger.Stub?.PrintStub(LogClass.ServiceAm, new { appletResourceUserId });
+
             return ResultCode.Success;
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
index 21572b9f29..db093dad06 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
@@ -24,6 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         private bool _vibrationPermitted;
         private bool _usbFullKeyControllerEnabled;
         private bool _isFirmwareUpdateAvailableForSixAxisSensor;
+        private bool _isSixAxisSensorUnalteredPassthroughEnabled;
 
         private HidNpadJoyAssignmentMode      _npadJoyAssignmentMode;
         private HidNpadHandheldActivationMode _npadHandheldActivationMode;
@@ -335,7 +336,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // StartSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode StartSixAxisSensor(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
@@ -347,7 +349,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // StopSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode StopSixAxisSensor(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
@@ -359,7 +362,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // IsSixAxisSensorFusionEnabled(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsEnabled
         public ResultCode IsSixAxisSensorFusionEnabled(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write(_sixAxisSensorFusionEnabled);
@@ -373,9 +377,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // EnableSixAxisSensorFusion(bool Enabled, nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode EnableSixAxisSensorFusion(ServiceCtx context)
         {
-            _sixAxisSensorFusionEnabled = context.RequestData.ReadBoolean();
-            int  sixAxisSensorHandle    = context.RequestData.ReadInt32();
-            long appletResourceUserId   = context.RequestData.ReadInt64();
+            _sixAxisSensorFusionEnabled = context.RequestData.ReadUInt32() != 0;
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            long appletResourceUserId = context.RequestData.ReadInt64();
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _sixAxisSensorFusionEnabled });
 
@@ -386,7 +390,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // SetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, float RevisePower, float ReviseRange, nn::applet::AppletResourceUserId)
         public ResultCode SetSixAxisSensorFusionParameters(ServiceCtx context)
         {
-            int   sixAxisSensorHandle = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
 
             _sensorFusionParams = new HidSensorFusionParameters
             {
@@ -405,7 +410,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // GetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float RevisePower, float ReviseRange)
         public ResultCode GetSixAxisSensorFusionParameters(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write(_sensorFusionParams.RevisePower);
@@ -420,7 +426,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // ResetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode ResetSixAxisSensorFusionParameters(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             _sensorFusionParams.RevisePower = 0;
@@ -436,6 +443,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         public ResultCode SetAccelerometerParameters(ServiceCtx context)
         {
             int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
 
             _accelerometerParams = new HidAccelerometerParameters
             {
@@ -454,7 +462,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // GetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float X, float Y
         public ResultCode GetAccelerometerParameters(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write(_accelerometerParams.X);
@@ -469,7 +478,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // ResetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode ResetAccelerometerParameters(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             _accelerometerParams.X = 0;
@@ -484,9 +494,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // SetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, uint PlayMode, nn::applet::AppletResourceUserId)
         public ResultCode SetAccelerometerPlayMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle    = context.RequestData.ReadInt32();
-                 _accelerometerPlayMode = context.RequestData.ReadUInt32();
-            long appletResourceUserId   = context.RequestData.ReadInt64();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
+            _accelerometerPlayMode = context.RequestData.ReadUInt32();
+            long appletResourceUserId = context.RequestData.ReadInt64();
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _accelerometerPlayMode });
 
@@ -497,7 +508,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // GetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> uint PlayMode
         public ResultCode GetAccelerometerPlayMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write(_accelerometerPlayMode);
@@ -511,7 +523,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // ResetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode ResetAccelerometerPlayMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             _accelerometerPlayMode = 0;
@@ -525,9 +538,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // SetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, uint GyroscopeZeroDriftMode, nn::applet::AppletResourceUserId)
         public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle     = context.RequestData.ReadInt32();
-                 _gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
-            long appletResourceUserId    = context.RequestData.ReadInt64();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            _gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
+            long appletResourceUserId = context.RequestData.ReadInt64();
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
 
@@ -538,7 +551,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // GetGyroscopeZeroDriftMode(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> int GyroscopeZeroDriftMode
         public ResultCode GetGyroscopeZeroDriftMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write((int)_gyroscopeZeroDriftMode);
@@ -552,7 +566,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // ResetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId)
         public ResultCode ResetGyroscopeZeroDriftMode(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             _gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
@@ -566,7 +581,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest
         public ResultCode IsSixAxisSensorAtRest(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             bool isAtRest = true;
@@ -582,8 +598,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // IsFirmwareUpdateAvailableForSixAxisSensor(nn::hid::AppletResourceUserId, nn::hid::SixAxisSensorHandle, pid) -> bool UpdateAvailable
         public ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(ServiceCtx context)
         {
-            int  sixAxisSensorHandle  = context.RequestData.ReadInt32();
-            context.RequestData.BaseStream.Position += 4;
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write(_isFirmwareUpdateAvailableForSixAxisSensor);
@@ -593,6 +609,64 @@ namespace Ryujinx.HLE.HOS.Services.Hid
             return ResultCode.Success;
         }
 
+        [CommandHipc(84)] // 13.0.0+
+        // EnableSixAxisSensorUnalteredPassthrough(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u8 enabled)
+        public ResultCode EnableSixAxisSensorUnalteredPassthrough(ServiceCtx context)
+        {
+            _isSixAxisSensorUnalteredPassthroughEnabled = context.RequestData.ReadUInt32() != 0;
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            long appletResourceUserId = context.RequestData.ReadInt64();
+
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _isSixAxisSensorUnalteredPassthroughEnabled });
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(85)] // 13.0.0+
+        // IsSixAxisSensorUnalteredPassthroughEnabled(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u8 enabled
+        public ResultCode IsSixAxisSensorUnalteredPassthroughEnabled(ServiceCtx context)
+        {
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
+            long appletResourceUserId = context.RequestData.ReadInt64();
+
+            context.ResponseData.Write(_isSixAxisSensorUnalteredPassthroughEnabled);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(87)] // 13.0.0+
+        // LoadSixAxisSensorCalibrationParameter(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle, u64 unknown)
+        public ResultCode LoadSixAxisSensorCalibrationParameter(ServiceCtx context)
+        {
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
+            long appletResourceUserId = context.RequestData.ReadInt64();
+
+            // TODO: CalibrationParameter have to be determined.
+
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(88)] // 13.0.0+
+        // GetSixAxisSensorIcInformation(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> u64 unknown
+        public ResultCode GetSixAxisSensorIcInformation(ServiceCtx context)
+        {
+            int sixAxisSensorHandle = context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
+            long appletResourceUserId = context.RequestData.ReadInt64();
+
+            // TODO: IcInformation have to be determined.
+
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle });
+
+            return ResultCode.Success;
+        }
+
         [CommandHipc(91)]
         // ActivateGesture(nn::applet::AppletResourceUserId, int Unknown0)
         public ResultCode ActivateGesture(ServiceCtx context)
@@ -606,16 +680,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         }
 
         [CommandHipc(100)]
-        // SetSupportedNpadStyleSet(nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
+        // SetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag)
         public ResultCode SetSupportedNpadStyleSet(ServiceCtx context)
         {
+            ulong pid = context.Request.HandleDesc.PId;
             ControllerType type = (ControllerType)context.RequestData.ReadInt32();
+            context.RequestData.BaseStream.Position += 4; // Padding
             long appletResourceUserId = context.RequestData.ReadInt64();
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
-                    appletResourceUserId,
-                    type
-                });
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { pid, appletResourceUserId, type });
 
             context.Device.Hid.Npads.SupportedStyleSets = type;
 
@@ -623,17 +696,15 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         }
 
         [CommandHipc(101)]
-        // GetSupportedNpadStyleSet(nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag
+        // GetSupportedNpadStyleSet(pid, nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag
         public ResultCode GetSupportedNpadStyleSet(ServiceCtx context)
         {
-            long appletResourceUserId = context.RequestData.ReadInt64();
+            ulong pid                  = context.Request.HandleDesc.PId;
+            long  appletResourceUserId = context.RequestData.ReadInt64();
 
             context.ResponseData.Write((int)context.Device.Hid.Npads.SupportedStyleSets);
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
-                    appletResourceUserId,
-                    context.Device.Hid.Npads.SupportedStyleSets
-                });
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, context.Device.Hid.Npads.SupportedStyleSets });
 
             return ResultCode.Success;
         }
@@ -658,7 +729,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
                 }
             }
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} " + string.Join(",", supportedPlayerIds.ToArray()));
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{supportedPlayerIds.Length} Players: " + string.Join(",", supportedPlayerIds.ToArray()));
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs
new file mode 100644
index 0000000000..8e52a94ef3
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Mnpp/IServiceForApplication.cs
@@ -0,0 +1,63 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.Cpu;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+
+namespace Ryujinx.HLE.HOS.Services.Mnpp
+{
+    [Service("mnpp:app")] // 13.0.0+
+    class IServiceForApplication : IpcService
+    {
+        public IServiceForApplication(ServiceCtx context) { }
+
+        [CommandHipc(0)]
+        // Initialize(pid)
+        public ResultCode Initialize(ServiceCtx context)
+        {
+            // Pid placeholder
+            context.RequestData.ReadInt64();
+            ulong pid = context.Request.HandleDesc.PId;
+
+            // TODO: Service calls set:sys GetPlatformRegion.
+            //       If the result == 1 (China) it calls arp:r GetApplicationInstanceId and GetApplicationLaunchProperty to get the title id and store it internally.
+            //       If not, it does nothing.
+
+            Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { pid });
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(1)]
+        // SendRawTelemetryData(nn::account::Uid user_id, buffer<bytes, 5> title_id)
+        public ResultCode SendRawTelemetryData(ServiceCtx context)
+        {
+            ulong titleIdInputPosition = context.Request.SendBuff[0].Position;
+            ulong titleIdInputSize     = context.Request.SendBuff[0].Size;
+
+            UserId userId = context.RequestData.ReadStruct<UserId>();
+
+            // TODO: Service calls set:sys GetPlatformRegion.
+            //       If the result != 1 (China) it returns ResultCode.Success.
+
+            if (userId.IsNull)
+            {
+                return ResultCode.InvalidArgument;
+            }
+
+            if (titleIdInputSize <= 64)
+            {
+                string titleId = MemoryHelper.ReadAsciiString(context.Memory, titleIdInputPosition, (long)titleIdInputSize);
+
+                // TODO: The service stores the titleId internally and seems proceed to some telemetry for China, which is not needed here.
+
+                Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { userId, titleId });
+
+                return ResultCode.Success;
+            }
+
+            Logger.Stub?.PrintStub(LogClass.ServiceMnpp, new { userId });
+
+            return ResultCode.InvalidBufferSize;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs
new file mode 100644
index 0000000000..dfc39a7352
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Mnpp/ResultCode.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Mnpp
+{
+    enum ResultCode
+    {
+        ModuleId       = 239,
+        ErrorCodeShift = 9,
+
+        Success = 0,
+
+        InvalidArgument   = (100 << ErrorCodeShift) | ModuleId,
+        InvalidBufferSize = (101 << ErrorCodeShift) | ModuleId
+    }
+}
\ No newline at end of file