diff options
| author | Ac_K <Acoustik666@gmail.com> | 2019-11-15 01:25:22 +0100 |
|---|---|---|
| committer | Thog <me@thog.eu> | 2019-11-15 01:25:22 +0100 |
| commit | 79abc6ed9375116a08eda6f8e722df56d029c373 (patch) | |
| tree | bba489695596fdfaf50ac42e54379458d2a74c35 /Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService | |
| parent | b9ae0b09d91fea3adc3ad10f9621ef8a4597c637 (diff) | |
Implement IApplicationFunctions & IQueryService commands (#823)
* Implement IApplicationFunctions & IQueryService commands
- Fix some nits in `IApplicationFunctions`
- Implement `QueryApplicationPlayStatistics` and `QueryApplicationPlayStatisticsByUid` checked by RE.
- Implement `QueryApplicationPlayStatisticsForSystem` and `QueryApplicationPlayStatisticsByUserAccountIdForSystem` checked by RE.
- Implement `QueryPlayStatisticsManager` to get/set played games statistics. We currently don't store any statistics because it's handled by qlaunch (or maybe am service?) on Switch.
We can add support later if games use returned statistics for something.
* Fix reviews
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService')
3 files changed, 104 insertions, 0 deletions
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 |
