aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs36
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/ResultCode.cs1
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs83
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs13
7 files changed, 161 insertions, 11 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
index f65509b5..464d0b47 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs
@@ -4,6 +4,8 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage;
+using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
+using Ryujinx.HLE.Utilities;
using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy
@@ -31,13 +33,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// EnsureSaveData(nn::account::Uid) -> u64
public ResultCode EnsureSaveData(ServiceCtx context)
{
- long uIdLow = context.RequestData.ReadInt64();
- long uIdHigh = context.RequestData.ReadInt64();
-
- Logger.PrintStub(LogClass.ServiceAm);
+ UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
context.ResponseData.Write(0L);
+ Logger.PrintStub(LogClass.ServiceAm, new { userId });
+
return ResultCode.Success;
}
@@ -54,9 +55,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// SetTerminateResult(u32)
public ResultCode SetTerminateResult(ServiceCtx context)
{
- int errorCode = context.RequestData.ReadInt32();
-
- string result = GetFormattedErrorCode(errorCode);
+ int errorCode = context.RequestData.ReadInt32();
+ string result = GetFormattedErrorCode(errorCode);
Logger.PrintInfo(LogClass.ServiceAm, $"Result = 0x{errorCode:x8} ({result}).");
@@ -95,11 +95,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// GetPseudoDeviceId() -> nn::util::Uuid
public ResultCode GetPseudoDeviceId(ServiceCtx context)
{
- Logger.PrintStub(LogClass.ServiceAm);
-
context.ResponseData.Write(0L);
context.ResponseData.Write(0L);
+ Logger.PrintStub(LogClass.ServiceAm);
+
return ResultCode.Success;
}
@@ -118,11 +118,27 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
{
int state = context.RequestData.ReadInt32();
- Logger.PrintStub(LogClass.ServiceAm);
+ Logger.PrintStub(LogClass.ServiceAm, new { state });
return ResultCode.Success;
}
+ [Command(110)] // 5.0.0+
+ // QueryApplicationPlayStatistics(buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
+ public ResultCode QueryApplicationPlayStatistics(ServiceCtx context)
+ {
+ // TODO: Call pdm:qry cmd 13 when IPC call between services will be implemented.
+ return (ResultCode)QueryPlayStatisticsManager.GetPlayStatistics(context);
+ }
+
+ [Command(111)] // 6.0.0+
+ // QueryApplicationPlayStatisticsByUid(nn::account::Uid, buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
+ public ResultCode QueryApplicationPlayStatisticsByUid(ServiceCtx context)
+ {
+ // TODO: Call pdm:qry cmd 16 when IPC call between services will be implemented.
+ return (ResultCode)QueryPlayStatisticsManager.GetPlayStatistics(context, true);
+ }
+
[Command(130)] // 8.0.0+
// GetGpuErrorDetectedSystemEvent() -> handle<copy>
public ResultCode GetGpuErrorDetectedSystemEvent(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs
index b46bd2b3..a5eb42f3 100644
--- a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
Success = 0,
NoMessages = (3 << ErrorCodeShift) | ModuleId,
+ ObjectInvalid = (500 << ErrorCodeShift) | ModuleId,
CpuBoostModeInvalid = (506 << ErrorCodeShift) | ModuleId
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs
index 61b26b8c..210dd98b 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/IQueryService.cs
@@ -1,8 +1,24 @@
-namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm
+using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
+
+namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm
{
[Service("pdm:qry")]
class IQueryService : IpcService
{
public IQueryService(ServiceCtx context) { }
+
+ [Command(13)] // 5.0.0+
+ // QueryApplicationPlayStatisticsForSystem(buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
+ public ResultCode QueryApplicationPlayStatisticsForSystem(ServiceCtx context)
+ {
+ return QueryPlayStatisticsManager.GetPlayStatistics(context);
+ }
+
+ [Command(16)] // 6.0.0+
+ // QueryApplicationPlayStatisticsByUserAccountIdForSystem(nn::account::Uid, buffer<bytes, 5> title_id_list) -> (buffer<bytes, 6> entries, s32 entries_count)
+ public ResultCode QueryApplicationPlayStatisticsByUserAccountIdForSystem(ServiceCtx context)
+ {
+ return QueryPlayStatisticsManager.GetPlayStatistics(context, true);
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs
new file mode 100644
index 00000000..b3646925
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs
@@ -0,0 +1,83 @@
+using ARMeilleure.Memory;
+using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService
+{
+ static class QueryPlayStatisticsManager
+ {
+ private static Dictionary<UInt128, ApplicationPlayStatistics> applicationPlayStatistics = new Dictionary<UInt128, ApplicationPlayStatistics>();
+
+ internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false)
+ {
+ long inputPosition = context.Request.SendBuff[0].Position;
+ long inputSize = context.Request.SendBuff[0].Size;
+
+ long outputPosition = context.Request.ReceiveBuff[0].Position;
+ long outputSize = context.Request.ReceiveBuff[0].Size;
+
+ UInt128 userId = byUserId ? new UInt128(context.RequestData.ReadBytes(0x10)) : new UInt128();
+
+ if (byUserId)
+ {
+ if (!context.Device.System.State.Account.TryGetUser(userId, out _))
+ {
+ return ResultCode.UserNotFound;
+ }
+ }
+
+ PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.System.ControlData.PlayLogQueryCapability;
+
+ List<ulong> titleIds = new List<ulong>();
+
+ for (int i = 0; i < inputSize / sizeof(ulong); i++)
+ {
+ titleIds.Add(BitConverter.ToUInt64(context.Memory.ReadBytes(inputPosition, inputSize), 0));
+ }
+
+ if (queryCapability == PlayLogQueryCapability.WhiteList)
+ {
+ // Check if input title ids are in the whitelist.
+ foreach (ulong titleId in titleIds)
+ {
+ if (!context.Device.System.ControlData.PlayLogQueryableApplicationId.Contains(titleId))
+ {
+ return (ResultCode)Am.ResultCode.ObjectInvalid;
+ }
+ }
+ }
+
+ MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
+
+ // Return ResultCode.ServiceUnavailable if data is locked by another process.
+ var filteredApplicationPlayStatistics = applicationPlayStatistics.AsEnumerable();
+
+ if (queryCapability == PlayLogQueryCapability.None)
+ {
+ filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => kv.Value.TitleId == context.Process.TitleId);
+ }
+ else // PlayLogQueryCapability.All
+ {
+ filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => titleIds.Contains(kv.Value.TitleId));
+ }
+
+ if (byUserId)
+ {
+ filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => kv.Key == userId);
+ }
+
+ for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++)
+ {
+ MemoryHelper.Write(context.Memory, outputPosition + (i * Marshal.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value);
+ }
+
+ context.ResponseData.Write(filteredApplicationPlayStatistics.Count());
+
+ return ResultCode.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs
new file mode 100644
index 00000000..c28d757e
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/ApplicationPlayStatistics.cs
@@ -0,0 +1,12 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types
+{
+ [StructLayout(LayoutKind.Sequential, Size = 0x18)]
+ struct ApplicationPlayStatistics
+ {
+ public ulong TitleId;
+ public long TotalPlayTime; // In nanoseconds.
+ public long TotalLaunchCount;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs
new file mode 100644
index 00000000..9e4b85de
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/Types/PlayLogQueryCapability.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types
+{
+ enum PlayLogQueryCapability
+ {
+ None,
+ WhiteList,
+ All
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs
new file mode 100644
index 00000000..3ceb8d1a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pdm/ResultCode.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm
+{
+ enum ResultCode
+ {
+ ModuleId = 178,
+ ErrorCodeShift = 9,
+
+ Success = 0,
+
+ UserNotFound = (101 << ErrorCodeShift) | ModuleId,
+ ServiceUnavailable = (150 << ErrorCodeShift) | ModuleId
+ }
+} \ No newline at end of file