From 3f3ae99a0fa048e5ad532b5ebe646eb572787626 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 25 Nov 2019 00:45:54 +0100 Subject: [PATCH] prepo: Implement calls of IPrepoService (#830) * prepo: Implement calls of IPrepoService - Implement `SaveReportOld`, `SaveReportWithUserOld`, `SaveReport`, `SaveReportWithUser` not accurate by RE (except for result codes). It's here to do something with the data since we will never use the `Play Report` sent by the game. So it's better than just a stub. - Fix a typo in `ldn` result code. Close #807 * Add a processing method * Address some comments * remove unneeded using * Resolve requested changes * Typo * Update IPrepoService.cs --- Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs | 2 +- .../HOS/Services/Prepo/IPrepoService.cs | 177 +++++++++++++++++- Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs | 15 ++ 3 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs diff --git a/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs index 0c9f6209..87674f7c 100644 --- a/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs @@ -11,6 +11,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn InvalidState = (32 << ErrorCodeShift) | ModuleId, Unknown1 = (48 << ErrorCodeShift) | ModuleId, InvalidArgument = (96 << ErrorCodeShift) | ModuleId, - InvalidOjbect = (97 << ErrorCodeShift) | ModuleId, + InvalidObject = (97 << ErrorCodeShift) | ModuleId, } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs index 94c027ab..fdd156d3 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -1,4 +1,9 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.Utilities; +using System; +using System.Buffers.Binary; +using System.IO; +using System.Text; namespace Ryujinx.HLE.HOS.Services.Prepo { @@ -9,13 +14,175 @@ namespace Ryujinx.HLE.HOS.Services.Prepo { public IPrepoService(ServiceCtx context) { } - [Command(10101)] - // SaveReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) - public static ResultCode SaveReportWithUser(ServiceCtx context) + [Command(10100)] // 1.0.0-5.1.0 + // SaveReport(u64, pid, buffer, buffer) + public ResultCode SaveReportOld(ServiceCtx context) { - Logger.PrintStub(LogClass.ServicePrepo); + // We don't care about the differences since we don't use the play report. + return ProcessReport(context, withUserID: false); + } + + [Command(10101)] // 1.0.0-5.1.0 + // SaveReportWithUserOld(nn::account::Uid, u64, pid, buffer, buffer) + public ResultCode SaveReportWithUserOld(ServiceCtx context) + { + // We don't care about the differences since we don't use the play report. + return ProcessReport(context, withUserID: true); + } + + [Command(10102)] // 6.0.0+ + // SaveReport(u64, pid, buffer, buffer) + public ResultCode SaveReport(ServiceCtx context) + { + // We don't care about the differences since we don't use the play report. + return ProcessReport(context, withUserID: false); + } + + [Command(10103)] // 6.0.0+ + // SaveReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) + public ResultCode SaveReportWithUser(ServiceCtx context) + { + // We don't care about the differences since we don't use the play report. + return ProcessReport(context, withUserID: true); + } + + private ResultCode ProcessReport(ServiceCtx context, bool withUserID) + { + UInt128 userId = withUserID ? new UInt128(context.RequestData.ReadBytes(0x10)) : new UInt128(); + string gameRoom = StringUtils.ReadUtf8String(context); + + if (withUserID) + { + if (userId.IsNull) + { + return ResultCode.InvalidArgument; + } + } + + if (gameRoom == string.Empty) + { + return ResultCode.InvalidState; + } + + long inputPosition = context.Request.SendBuff[0].Position; + long inputSize = context.Request.SendBuff[0].Size; + + if (inputSize == 0) + { + return ResultCode.InvalidBufferSize; + } + + byte[] inputBuffer = context.Memory.ReadBytes(inputPosition, inputSize); + + Logger.PrintInfo(LogClass.ServicePrepo, ReadReportBuffer(inputBuffer, gameRoom, userId)); return ResultCode.Success; } + + public string ReadReportBuffer(byte[] buffer, string room, UInt128 userId) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendLine(); + sb.AppendLine("PlayReport log:"); + + if (!userId.IsNull) + { + sb.AppendLine($" UserId: {userId.ToString()}"); + } + + sb.AppendLine($" Room: {room}"); + + using (MemoryStream stream = new MemoryStream(buffer)) + using (BinaryReader reader = new BinaryReader(stream)) + { + byte unknown1 = reader.ReadByte(); // Version ? + short unknown2 = reader.ReadInt16(); // Size ? + + bool isValue = false; + + string fieldStr = string.Empty; + + while (stream.Position != stream.Length) + { + byte descriptor = reader.ReadByte(); + + if (!isValue) + { + byte[] key = reader.ReadBytes(descriptor - 0xA0); + + fieldStr = $" Key: {Encoding.ASCII.GetString(key)}"; + + isValue = true; + } + else + { + if (descriptor > 0xD0) // Int value. + { + if (descriptor - 0xD0 == 1) + { + fieldStr += $", Value: {BinaryPrimitives.ReverseEndianness(reader.ReadUInt16())}"; + } + else if (descriptor - 0xD0 == 2) + { + fieldStr += $", Value: {BinaryPrimitives.ReverseEndianness(reader.ReadInt32())}"; + } + else if (descriptor - 0xD0 == 4) + { + fieldStr += $", Value: {BinaryPrimitives.ReverseEndianness(reader.ReadInt64())}"; + } + else + { + // Unknown. + break; + } + } + else if (descriptor > 0xA0 && descriptor < 0xD0) // String value, max size = 0x20 bytes ? + { + int size = descriptor - 0xA0; + string value = string.Empty; + byte[] rawValues = new byte[0]; + + for (int i = 0; i < size; i++) + { + byte chr = reader.ReadByte(); + + if (chr >= 0x20 && chr < 0x7f) + { + value += (char)chr; + } + else + { + Array.Resize(ref rawValues, rawValues.Length + 1); + + rawValues[rawValues.Length - 1] = chr; + } + } + + if (value != string.Empty) + { + fieldStr += $", Value: {value}"; + } + + // TODO(Ac_K): Determine why there are non-alphanumeric values sometimes. + if (rawValues.Length > 0) + { + fieldStr += $", RawValue: 0x{BitConverter.ToString(rawValues).Replace("-", "")}"; + } + } + else // Byte value. + { + fieldStr += $", Value: {descriptor}"; + } + + sb.AppendLine(fieldStr); + + isValue = false; + } + } + } + + return sb.ToString(); + } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs new file mode 100644 index 00000000..1e110ea6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Prepo/ResultCode.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.HLE.HOS.Services.Prepo +{ + enum ResultCode + { + ModuleId = 129, + ErrorCodeShift = 9, + + Success = 0, + + InvalidArgument = (1 << ErrorCodeShift) | ModuleId, + InvalidState = (5 << ErrorCodeShift) | ModuleId, + InvalidBufferSize = (9 << ErrorCodeShift) | ModuleId, + Unknown1 = (90 << ErrorCodeShift) | ModuleId + } +} \ No newline at end of file