QueryPlayStatisticsManager.cs
1 using Ryujinx.Common; 2 using Ryujinx.Cpu; 3 using Ryujinx.HLE.HOS.Services.Account.Acc; 4 using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types; 5 using System; 6 using System.Collections.Generic; 7 using System.Linq; 8 using System.Runtime.CompilerServices; 9 10 namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService 11 { 12 class QueryPlayStatisticsManager 13 { 14 private static readonly Dictionary<UserId, ApplicationPlayStatistics> _applicationPlayStatistics = new(); 15 16 internal static ResultCode GetPlayStatistics(ServiceCtx context, bool byUserId = false) 17 { 18 ulong inputPosition = context.Request.SendBuff[0].Position; 19 ulong inputSize = context.Request.SendBuff[0].Size; 20 21 ulong outputPosition = context.Request.ReceiveBuff[0].Position; 22 ulong outputSize = context.Request.ReceiveBuff[0].Size; 23 24 UserId userId = byUserId ? context.RequestData.ReadStruct<UserId>() : new UserId(); 25 26 if (byUserId) 27 { 28 if (!context.Device.System.AccountManager.TryGetUser(userId, out _)) 29 { 30 return ResultCode.UserNotFound; 31 } 32 } 33 34 PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryCapability; 35 36 List<ulong> titleIds = new(); 37 38 for (ulong i = 0; i < inputSize / sizeof(ulong); i++) 39 { 40 titleIds.Add(context.Memory.Read<ulong>(inputPosition)); 41 } 42 43 if (queryCapability == PlayLogQueryCapability.WhiteList) 44 { 45 // Check if input title ids are in the whitelist. 46 foreach (ulong titleId in titleIds) 47 { 48 if (!context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryableApplicationId.ItemsRo.Contains(titleId)) 49 { 50 return (ResultCode)Am.ResultCode.ObjectInvalid; 51 } 52 } 53 } 54 55 MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); 56 57 // Return ResultCode.ServiceUnavailable if data is locked by another process. 58 var filteredApplicationPlayStatistics = _applicationPlayStatistics.AsEnumerable(); 59 60 if (queryCapability == PlayLogQueryCapability.None) 61 { 62 filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => kv.Value.TitleId == context.Process.TitleId); 63 } 64 else // PlayLogQueryCapability.All 65 { 66 filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => titleIds.Contains(kv.Value.TitleId)); 67 } 68 69 if (byUserId) 70 { 71 filteredApplicationPlayStatistics = filteredApplicationPlayStatistics.Where(kv => kv.Key == userId); 72 } 73 74 for (int i = 0; i < filteredApplicationPlayStatistics.Count(); i++) 75 { 76 MemoryHelper.Write(context.Memory, outputPosition + (ulong)(i * Unsafe.SizeOf<ApplicationPlayStatistics>()), filteredApplicationPlayStatistics.ElementAt(i).Value); 77 } 78 79 context.ResponseData.Write(filteredApplicationPlayStatistics.Count()); 80 81 return ResultCode.Success; 82 } 83 } 84 }