diff --git a/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Ldn/ResultCode.cs index 0c9f6209cd..87674f7c39 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 94c027ab3b..2c605cdd56 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -1,4 +1,9 @@ +using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.HLE.Utilities; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; namespace Ryujinx.HLE.HOS.Services.Prepo { @@ -7,15 +12,153 @@ namespace Ryujinx.HLE.HOS.Services.Prepo [Service("prepo:u")] class IPrepoService : IpcService { + private bool _withUserId; + 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); + _withUserId = false; + + // We don't care about the differences since we don't use the play report. + return SaveReportWithUser(context); + } + + [Command(10101)] // 1.0.0-5.1.0 + // SaveReportWithUserOld(nn::account::Uid, u64, pid, buffer, buffer) + public ResultCode SaveReportWithUserOld(ServiceCtx context) + { + _withUserId = true; + + // We don't care about the differences since we don't use the play report. + return SaveReportWithUser(context); + } + + [Command(10102)] // 6.0.0+ + // SaveReport(u64, pid, buffer, buffer) + public ResultCode SaveReport(ServiceCtx context) + { + _withUserId = false; + + // We don't care about the differences since we don't use the play report. + return SaveReportWithUser(context); + } + + [Command(10103)] // 6.0.0+ + // SaveReportWithUser(nn::account::Uid, u64, pid, buffer, buffer) + public ResultCode SaveReportWithUser(ServiceCtx context) + { + 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 == "") + { + 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("\nPlayReport log:"); + + if (_withUserId) + { + 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 = ""; + + while (stream.Position != stream.Length) + { + byte flag = reader.ReadByte(); + + if (!isValue) + { + byte[] key = reader.ReadBytes(flag - 0xA0); + + fieldStr = $" Key: {Encoding.ASCII.GetString(key)}"; + + isValue = true; + } + else + { + if (flag > 0xD0) // Int value. + { + if (flag - 0xD0 == 1) + { + fieldStr += $", Value: {EndianSwap.Swap16(reader.ReadUInt16())}"; + } + else if (flag - 0xD0 == 2) + { + fieldStr += $", Value: {EndianSwap.Swap32(reader.ReadInt32())}"; + } + else if (flag - 0xD0 == 4) + { + fieldStr += $", Value: {reader.ReadInt64()}"; + } + else + { + // Unknown. + break; + } + } + else if (flag > 0xA0 && flag < 0xD0) // String value, max size = 0x20 bytes ? + { + byte[] valueBuffer = reader.ReadBytes(flag - 0xA0); + string value = Encoding.ASCII.GetString(valueBuffer); + + // TODO: Find why there is no alpha-numeric value sometimes. + fieldStr += $", Value: {Regex.Replace(value, "[^A-Za-z0-9 -~]", "")}"; + } + else // Byte value. + { + fieldStr += $", Value: {flag}"; + } + + 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 0000000000..6d488d0df6 --- /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