diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs index 0b10fa36..4a7d7bb3 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs @@ -1,9 +1,14 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.Common.Utilities; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService; +using System; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Security.Cryptography; namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator { @@ -186,5 +191,66 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator return ResultCode.Success; } + + [Command(10700)] + // nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer + public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context) + { + bool unknownBool = context.RequestData.ReadBoolean(); + UserId userId = context.RequestData.ReadStruct(); + + long bufferPosition = context.Request.RecvListBuff[0].Position; + + if (userId.IsNull) + { + return ResultCode.InvalidArgument; + } + + // NOTE: Calls nn::friends::detail::service::core::PlayHistoryManager::GetInstance and stores the instance. + + byte[] randomBytes = new byte[8]; + Random random = new Random(); + + random.NextBytes(randomBytes); + + // NOTE: Calls nn::friends::detail::service::core::UuidManager::GetInstance and stores the instance. + // Then call nn::friends::detail::service::core::AccountStorageManager::GetInstance and store the instance. + // Then it checks if an Uuid is already stored for the UserId, if not it generates a random Uuid. + // And store it in the savedata 8000000000000080 in the friends:/uid.bin file. + + Array16 randomGuid = new Array16(); + + Guid.NewGuid().ToByteArray().AsSpan().CopyTo(randomGuid.ToSpan()); + + PlayHistoryRegistrationKey playHistoryRegistrationKey = new PlayHistoryRegistrationKey + { + Type = 0x101, + KeyIndex = (byte)(randomBytes[0] & 7), + UserIdBool = 0, // TODO: Find it. + UnknownBool = (byte)(unknownBool ? 1 : 0), // TODO: Find it. + Reserved = new Array11(), + Uuid = randomGuid + }; + + ReadOnlySpan playHistoryRegistrationKeyBuffer = SpanHelpers.AsByteSpan(ref playHistoryRegistrationKey); + + /* + + NOTE: The service uses the KeyIndex to get a random key from a keys buffer (since the key index is stored in the returned buffer). + We currently don't support play history and online services so we can use a blank key for now. + Code for reference: + + byte[] hmacKey = new byte[0x20]; + + HMACSHA256 hmacSha256 = new HMACSHA256(hmacKey); + byte[] hmacHash = hmacSha256.ComputeHash(playHistoryRegistrationKeyBuffer); + + */ + + context.Memory.Write((ulong)bufferPosition, playHistoryRegistrationKeyBuffer); + context.Memory.Write((ulong)bufferPosition + 0x20, new byte[0x20]); // HmacHash + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs new file mode 100644 index 00000000..32d962c1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs @@ -0,0 +1,16 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator +{ + [StructLayout(LayoutKind.Sequential, Size = 0x20)] + struct PlayHistoryRegistrationKey + { + public ushort Type; + public byte KeyIndex; + public byte UserIdBool; + public byte UnknownBool; + public Array11 Reserved; + public Array16 Uuid; + } +}