aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE')
-rw-r--r--src/Ryujinx.HLE/HOS/Horizon.cs2
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs13
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs55
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs14
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs29
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs24
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs9
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs10
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs34
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs14
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs374
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs178
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs74
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs9
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs13
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs19
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs16
17 files changed, 13 insertions, 874 deletions
diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs
index 1a402240..cd171958 100644
--- a/src/Ryujinx.HLE/HOS/Horizon.cs
+++ b/src/Ryujinx.HLE/HOS/Horizon.cs
@@ -330,7 +330,7 @@ namespace Ryujinx.HLE.HOS
HorizonFsClient fsClient = new(this);
ServiceTable = new ServiceTable();
- var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient));
+ var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices, LibHacHorizonManager.BcatClient, fsClient, AccountManager));
foreach (var service in services)
{
diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
index 924ac3fb..c724660e 100644
--- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs
@@ -4,6 +4,7 @@ using LibHac.Fs;
using LibHac.Fs.Shim;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
+using Ryujinx.Horizon.Sdk.Account;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@@ -11,7 +12,7 @@ using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
- public class AccountManager
+ public class AccountManager : IEmulatorAccountManager
{
public static readonly UserId DefaultUserId = new("00000000000000010000000000000000");
@@ -106,6 +107,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
_accountSaveDataManager.Save(_profiles);
}
+ public void OpenUserOnlinePlay(Uid userId)
+ {
+ OpenUserOnlinePlay(new UserId((long)userId.Low, (long)userId.High));
+ }
+
public void OpenUserOnlinePlay(UserId userId)
{
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
@@ -127,6 +133,11 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
_accountSaveDataManager.Save(_profiles);
}
+ public void CloseUserOnlinePlay(Uid userId)
+ {
+ CloseUserOnlinePlay(new UserId((long)userId.Low, (long)userId.High));
+ }
+
public void CloseUserOnlinePlay(UserId userId)
{
if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile))
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs
deleted file mode 100644
index 3f15f3fc..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator;
-
-namespace Ryujinx.HLE.HOS.Services.Friend
-{
- [Service("friend:a", FriendServicePermissionLevel.Administrator)]
- [Service("friend:m", FriendServicePermissionLevel.Manager)]
- [Service("friend:s", FriendServicePermissionLevel.System)]
- [Service("friend:u", FriendServicePermissionLevel.User)]
- [Service("friend:v", FriendServicePermissionLevel.Viewer)]
- class IServiceCreator : IpcService
- {
- private readonly FriendServicePermissionLevel _permissionLevel;
-
- public IServiceCreator(ServiceCtx context, FriendServicePermissionLevel permissionLevel)
- {
- _permissionLevel = permissionLevel;
- }
-
- [CommandCmif(0)]
- // CreateFriendService() -> object<nn::friends::detail::ipc::IFriendService>
- public ResultCode CreateFriendService(ServiceCtx context)
- {
- MakeObject(context, new IFriendService(_permissionLevel));
-
- return ResultCode.Success;
- }
-
- [CommandCmif(1)] // 2.0.0+
- // CreateNotificationService(nn::account::Uid userId) -> object<nn::friends::detail::ipc::INotificationService>
- public ResultCode CreateNotificationService(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- MakeObject(context, new INotificationService(context, userId, _permissionLevel));
-
- return ResultCode.Success;
- }
-
- [CommandCmif(2)] // 4.0.0+
- // CreateDaemonSuspendSessionService() -> object<nn::friends::detail::ipc::IDaemonSuspendSessionService>
- public ResultCode CreateDaemonSuspendSessionService(ServiceCtx context)
- {
- MakeObject(context, new IDaemonSuspendSessionService(_permissionLevel));
-
- return ResultCode.Success;
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs
deleted file mode 100644
index 9f612059..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ResultCode.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Friend
-{
- enum ResultCode
- {
- ModuleId = 121,
- ErrorCodeShift = 9,
-
- Success = 0,
-
- InvalidArgument = (2 << ErrorCodeShift) | ModuleId,
- InternetRequestDenied = (6 << ErrorCodeShift) | ModuleId,
- NotificationQueueEmpty = (15 << ErrorCodeShift) | ModuleId,
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs
deleted file mode 100644
index 28745c3f..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
-{
- [StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x200, CharSet = CharSet.Ansi)]
- struct Friend
- {
- public UserId UserId;
- public long NetworkUserId;
-
- [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x21)]
- public string Nickname;
-
- public UserPresence presence;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsFavourite;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsNew;
-
- [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x6)]
- readonly char[] Unknown;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsValid;
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs
deleted file mode 100644
index 5f13f313..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
-{
- [StructLayout(LayoutKind.Sequential)]
- struct FriendFilter
- {
- public PresenceStatusFilter PresenceStatus;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsFavoriteOnly;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsSameAppPresenceOnly;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsSameAppPlayedOnly;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool IsArbitraryAppPlayedOnly;
-
- public long PresenceGroupId;
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs
deleted file mode 100644
index 7930aff0..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
-{
- enum PresenceStatus : uint
- {
- Offline,
- Online,
- OnlinePlay,
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs
deleted file mode 100644
index c9a54250..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
-{
- enum PresenceStatusFilter : uint
- {
- None,
- Online,
- OnlinePlay,
- OnlineOrOnlinePlay,
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs
deleted file mode 100644
index 80d14205..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Ryujinx.Common.Memory;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
-{
- [StructLayout(LayoutKind.Sequential, Pack = 0x8)]
- struct UserPresence
- {
- public UserId UserId;
- public long LastTimeOnlineTimestamp;
- public PresenceStatus Status;
-
- [MarshalAs(UnmanagedType.I1)]
- public bool SamePresenceGroupApplication;
-
- public Array3<byte> Unknown;
- private AppKeyValueStorageHolder _appKeyValueStorage;
-
- public Span<byte> AppKeyValueStorage => MemoryMarshal.Cast<AppKeyValueStorageHolder, byte>(MemoryMarshal.CreateSpan(ref _appKeyValueStorage, AppKeyValueStorageHolder.Size));
-
- [StructLayout(LayoutKind.Sequential, Pack = 0x1, Size = Size)]
- private struct AppKeyValueStorageHolder
- {
- public const int Size = 0xC0;
- }
-
- public readonly override string ToString()
- {
- return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status} }}";
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs
deleted file mode 100644
index 3b1601ab..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
-{
- class IDaemonSuspendSessionService : IpcService
- {
-#pragma warning disable IDE0052 // Remove unread private member
- private readonly FriendServicePermissionLevel _permissionLevel;
-#pragma warning restore IDE0052
-
- public IDaemonSuspendSessionService(FriendServicePermissionLevel permissionLevel)
- {
- _permissionLevel = permissionLevel;
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
deleted file mode 100644
index 54d23e88..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
+++ /dev/null
@@ -1,374 +0,0 @@
-using LibHac.Ns;
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
-using Ryujinx.Common.Utilities;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService;
-using Ryujinx.Horizon.Common;
-using System;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
-{
- class IFriendService : IpcService
- {
-#pragma warning disable IDE0052 // Remove unread private member
- private readonly FriendServicePermissionLevel _permissionLevel;
-#pragma warning restore IDE0052
- private KEvent _completionEvent;
-
- public IFriendService(FriendServicePermissionLevel permissionLevel)
- {
- _permissionLevel = permissionLevel;
- }
-
- [CommandCmif(0)]
- // GetCompletionEvent() -> handle<copy>
- public ResultCode GetCompletionEvent(ServiceCtx context)
- {
- _completionEvent ??= new KEvent(context.Device.System.KernelContext);
-
- if (context.Process.HandleTable.GenerateHandle(_completionEvent.ReadableEvent, out int completionEventHandle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
-
- _completionEvent.WritableEvent.Signal();
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(completionEventHandle);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(1)]
- // nn::friends::Cancel()
- public ResultCode Cancel(ServiceCtx context)
- {
- // TODO: Original service sets an internal field to 1 here. Determine usage.
- Logger.Stub?.PrintStub(LogClass.ServiceFriend);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10100)]
- // nn::friends::GetFriendListIds(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
- // -> int outCount, array<nn::account::NetworkServiceAccountId, 0xa>
- public ResultCode GetFriendListIds(ServiceCtx context)
- {
- int offset = context.RequestData.ReadInt32();
-
- // Padding
- context.RequestData.ReadInt32();
-
- UserId userId = context.RequestData.ReadStruct<UserId>();
- FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
-
- // Pid placeholder
- context.RequestData.ReadInt64();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
- context.ResponseData.Write(0);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new
- {
- UserId = userId.ToString(),
- offset,
- filter.PresenceStatus,
- filter.IsFavoriteOnly,
- filter.IsSameAppPresenceOnly,
- filter.IsSameAppPlayedOnly,
- filter.IsArbitraryAppPlayedOnly,
- filter.PresenceGroupId,
- });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10101)]
- // nn::friends::GetFriendList(int offset, nn::account::Uid userId, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid)
- // -> int outCount, array<nn::friends::detail::FriendImpl, 0x6>
- public ResultCode GetFriendList(ServiceCtx context)
- {
- int offset = context.RequestData.ReadInt32();
-
- // Padding
- context.RequestData.ReadInt32();
-
- UserId userId = context.RequestData.ReadStruct<UserId>();
- FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
-
- // Pid placeholder
- context.RequestData.ReadInt64();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
- context.ResponseData.Write(0);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new
- {
- UserId = userId.ToString(),
- offset,
- filter.PresenceStatus,
- filter.IsFavoriteOnly,
- filter.IsSameAppPresenceOnly,
- filter.IsSameAppPlayedOnly,
- filter.IsArbitraryAppPlayedOnly,
- filter.PresenceGroupId,
- });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10120)] // 10.0.0+
- // nn::friends::IsFriendListCacheAvailable(nn::account::Uid userId) -> bool
- public ResultCode IsFriendListCacheAvailable(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- // TODO: Service mount the friends:/ system savedata and try to load friend.cache file, returns true if exists, false otherwise.
- // NOTE: If no cache is available, guest then calls nn::friends::EnsureFriendListAvailable, we can avoid that by faking the cache check.
- context.ResponseData.Write(true);
-
- // TODO: Since we don't support friend features, it's fine to stub it for now.
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10121)] // 10.0.0+
- // nn::friends::EnsureFriendListAvailable(nn::account::Uid userId)
- public ResultCode EnsureFriendListAvailable(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- // TODO: Service mount the friends:/ system savedata and create a friend.cache file for the given user id.
- // Since we don't support friend features, it's fine to stub it for now.
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10400)]
- // nn::friends::GetBlockedUserListIds(int offset, nn::account::Uid userId) -> (u32, buffer<nn::account::NetworkServiceAccountId, 0xa>)
- public ResultCode GetBlockedUserListIds(ServiceCtx context)
- {
- int offset = context.RequestData.ReadInt32();
-
- // Padding
- context.RequestData.ReadInt32();
-
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- // There are no friends blocked, so we return 0 because the nn::account::NetworkServiceAccountId array is empty.
- context.ResponseData.Write(0);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { offset, UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10420)]
- // nn::friends::CheckBlockedUserListAvailability(nn::account::Uid userId) -> bool
- public ResultCode CheckBlockedUserListAvailability(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- // Yes, it is available.
- context.ResponseData.Write(true);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10600)]
- // nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid userId)
- public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- context.Device.System.AccountManager.OpenUserOnlinePlay(userId);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10601)]
- // nn::friends::DeclareCloseOnlinePlaySession(nn::account::Uid userId)
- public ResultCode DeclareCloseOnlinePlaySession(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- if (userId.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- context.Device.System.AccountManager.CloseUserOnlinePlay(userId);
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10610)]
- // nn::friends::UpdateUserPresence(nn::account::Uid, u64, pid, buffer<nn::friends::detail::UserPresenceImpl, 0x19>)
- public ResultCode UpdateUserPresence(ServiceCtx context)
- {
- UserId uuid = context.RequestData.ReadStruct<UserId>();
-
- // Pid placeholder
- context.RequestData.ReadInt64();
-
- ulong position = context.Request.PtrBuff[0].Position;
- ulong size = context.Request.PtrBuff[0].Size;
-
- ReadOnlySpan<UserPresence> userPresenceInputArray = MemoryMarshal.Cast<byte, UserPresence>(context.Memory.GetSpan(position, (int)size));
-
- if (uuid.IsNull)
- {
- return ResultCode.InvalidArgument;
- }
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray = userPresenceInputArray.ToArray() });
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10700)]
- // nn::friends::GetPlayHistoryRegistrationKey(b8 unknown, nn::account::Uid) -> buffer<nn::friends::PlayHistoryRegistrationKey, 0x1a>
- public ResultCode GetPlayHistoryRegistrationKey(ServiceCtx context)
- {
- bool unknownBool = context.RequestData.ReadBoolean();
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x40UL);
-
- ulong 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.Shared.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<byte> randomGuid = new();
-
- Guid.NewGuid().ToByteArray().AsSpan().CopyTo(randomGuid.AsSpan());
-
- PlayHistoryRegistrationKey playHistoryRegistrationKey = new()
- {
- Type = 0x101,
- KeyIndex = (byte)(randomBytes[0] & 7),
- UserIdBool = 0, // TODO: Find it.
- UnknownBool = (byte)(unknownBool ? 1 : 0), // TODO: Find it.
- Reserved = new Array11<byte>(),
- Uuid = randomGuid,
- };
-
- ReadOnlySpan<byte> 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(bufferPosition, playHistoryRegistrationKeyBuffer);
- context.Memory.Write(bufferPosition + 0x20, new byte[0x20]); // HmacHash
-
- return ResultCode.Success;
- }
-
- [CommandCmif(10702)]
- // nn::friends::AddPlayHistory(nn::account::Uid, u64, pid, buffer<nn::friends::PlayHistoryRegistrationKey, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>, buffer<nn::friends::InAppScreenName, 0x19>)
- public ResultCode AddPlayHistory(ServiceCtx context)
- {
- UserId userId = context.RequestData.ReadStruct<UserId>();
-
- // Pid placeholder
- context.RequestData.ReadInt64();
-#pragma warning disable IDE0059 // Remove unnecessary value assignment
- ulong pid = context.Request.HandleDesc.PId;
-
- ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position;
- ulong playHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size;
-
- ulong inAppScreenName1Position = context.Request.PtrBuff[1].Position;
-#pragma warning restore IDE0059
- ulong inAppScreenName1Size = context.Request.PtrBuff[1].Size;
-
-#pragma warning disable IDE0059 // Remove unnecessary value assignment
- ulong inAppScreenName2Position = context.Request.PtrBuff[2].Position;
-#pragma warning restore IDE0059
- ulong inAppScreenName2Size = context.Request.PtrBuff[2].Size;
-
- if (userId.IsNull || inAppScreenName1Size > 0x48 || inAppScreenName2Size > 0x48)
- {
- return ResultCode.InvalidArgument;
- }
-
- // TODO: Call nn::arp::GetApplicationControlProperty here when implemented.
-#pragma warning disable IDE0059 // Remove unnecessary value assignment
- ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
-#pragma warning restore IDE0059
-
- /*
-
- NOTE: The service calls nn::friends::detail::service::core::PlayHistoryManager to store informations using the registration key computed in GetPlayHistoryRegistrationKey.
- Then calls nn::friends::detail::service::core::FriendListManager to update informations on the friend list.
- We currently don't support play history and online services so it's fine to do nothing.
-
- */
-
- Logger.Stub?.PrintStub(LogClass.ServiceFriend);
-
- return ResultCode.Success;
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs
deleted file mode 100644
index 8fc7a460..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs
+++ /dev/null
@@ -1,178 +0,0 @@
-using Ryujinx.Common;
-using Ryujinx.HLE.HOS.Ipc;
-using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService;
-using Ryujinx.Horizon.Common;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
-{
- class INotificationService : DisposableIpcService
- {
- private readonly UserId _userId;
- private readonly FriendServicePermissionLevel _permissionLevel;
-
- private readonly object _lock = new();
-
- private readonly KEvent _notificationEvent;
- private int _notificationEventHandle = 0;
-
- private readonly LinkedList<NotificationInfo> _notifications;
-
- private bool _hasNewFriendRequest;
- private bool _hasFriendListUpdate;
-
- public INotificationService(ServiceCtx context, UserId userId, FriendServicePermissionLevel permissionLevel)
- {
- _userId = userId;
- _permissionLevel = permissionLevel;
- _notifications = new LinkedList<NotificationInfo>();
- _notificationEvent = new KEvent(context.Device.System.KernelContext);
-
- _hasNewFriendRequest = false;
- _hasFriendListUpdate = false;
-
- NotificationEventHandler.Instance.RegisterNotificationService(this);
- }
-
- [CommandCmif(0)] //2.0.0+
- // nn::friends::detail::ipc::INotificationService::GetEvent() -> handle<copy>
- public ResultCode GetEvent(ServiceCtx context)
- {
- if (_notificationEventHandle == 0)
- {
- if (context.Process.HandleTable.GenerateHandle(_notificationEvent.ReadableEvent, out _notificationEventHandle) != Result.Success)
- {
- throw new InvalidOperationException("Out of handles!");
- }
- }
-
- context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationEventHandle);
-
- return ResultCode.Success;
- }
-
- [CommandCmif(1)] //2.0.0+
- // nn::friends::detail::ipc::INotificationService::Clear()
- public ResultCode Clear(ServiceCtx context)
- {
- lock (_lock)
- {
- _hasNewFriendRequest = false;
- _hasFriendListUpdate = false;
-
- _notifications.Clear();
- }
-
- return ResultCode.Success;
- }
-
- [CommandCmif(2)] // 2.0.0+
- // nn::friends::detail::ipc::INotificationService::Pop() -> nn::friends::detail::ipc::SizedNotificationInfo
- public ResultCode Pop(ServiceCtx context)
- {
- lock (_lock)
- {
- if (_notifications.Count >= 1)
- {
- NotificationInfo notificationInfo = _notifications.First.Value;
- _notifications.RemoveFirst();
-
- if (notificationInfo.Type == NotificationEventType.FriendListUpdate)
- {
- _hasFriendListUpdate = false;
- }
- else if (notificationInfo.Type == NotificationEventType.NewFriendRequest)
- {
- _hasNewFriendRequest = false;
- }
-
- context.ResponseData.WriteStruct(notificationInfo);
-
- return ResultCode.Success;
- }
- }
-
- return ResultCode.NotificationQueueEmpty;
- }
-
- public void SignalFriendListUpdate(UserId targetId)
- {
- lock (_lock)
- {
- if (_userId == targetId)
- {
- if (!_hasFriendListUpdate)
- {
- NotificationInfo friendListNotification = new();
-
- if (_notifications.Count != 0)
- {
- friendListNotification = _notifications.First.Value;
- _notifications.RemoveFirst();
- }
-
- friendListNotification.Type = NotificationEventType.FriendListUpdate;
- _hasFriendListUpdate = true;
-
- if (_hasNewFriendRequest)
- {
- NotificationInfo newFriendRequestNotification = new();
-
- if (_notifications.Count != 0)
- {
- newFriendRequestNotification = _notifications.First.Value;
- _notifications.RemoveFirst();
- }
-
- newFriendRequestNotification.Type = NotificationEventType.NewFriendRequest;
- _notifications.AddFirst(newFriendRequestNotification);
- }
-
- // We defer this to make sure we are on top of the queue.
- _notifications.AddFirst(friendListNotification);
- }
-
- _notificationEvent.ReadableEvent.Signal();
- }
- }
- }
-
- public void SignalNewFriendRequest(UserId targetId)
- {
- lock (_lock)
- {
- if ((_permissionLevel & FriendServicePermissionLevel.ViewerMask) != 0 && _userId == targetId)
- {
- if (!_hasNewFriendRequest)
- {
- if (_notifications.Count == 100)
- {
- SignalFriendListUpdate(targetId);
- }
-
- NotificationInfo newFriendRequestNotification = new()
- {
- Type = NotificationEventType.NewFriendRequest,
- };
-
- _notifications.AddLast(newFriendRequestNotification);
- _hasNewFriendRequest = true;
- }
-
- _notificationEvent.ReadableEvent.Signal();
- }
- }
- }
-
- protected override void Dispose(bool isDisposing)
- {
- if (isDisposing)
- {
- NotificationEventHandler.Instance.UnregisterNotificationService(this);
- }
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs
deleted file mode 100644
index 88627fd7..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using Ryujinx.HLE.HOS.Services.Account.Acc;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
-{
- public sealed class NotificationEventHandler
- {
- private static NotificationEventHandler _instance;
- private static readonly object _instanceLock = new();
-
- private readonly INotificationService[] _registry;
-
- public static NotificationEventHandler Instance
- {
- get
- {
- lock (_instanceLock)
- {
- _instance ??= new NotificationEventHandler();
-
- return _instance;
- }
- }
- }
-
- NotificationEventHandler()
- {
- _registry = new INotificationService[0x20];
- }
-
- internal void RegisterNotificationService(INotificationService service)
- {
- // NOTE: in case there isn't space anymore in the registry array, Nintendo doesn't return any errors.
- for (int i = 0; i < _registry.Length; i++)
- {
- if (_registry[i] == null)
- {
- _registry[i] = service;
- break;
- }
- }
- }
-
- internal void UnregisterNotificationService(INotificationService service)
- {
- // NOTE: in case there isn't the entry in the registry array, Nintendo doesn't return any errors.
- for (int i = 0; i < _registry.Length; i++)
- {
- if (_registry[i] == service)
- {
- _registry[i] = null;
- break;
- }
- }
- }
-
- // TODO: Use this when we will have enough things to go online.
- public void SignalFriendListUpdate(UserId targetId)
- {
- for (int i = 0; i < _registry.Length; i++)
- {
- _registry[i]?.SignalFriendListUpdate(targetId);
- }
- }
-
- // TODO: Use this when we will have enough things to go online.
- public void SignalNewFriendRequest(UserId targetId)
- {
- for (int i = 0; i < _registry.Length; i++)
- {
- _registry[i]?.SignalNewFriendRequest(targetId);
- }
- }
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs
deleted file mode 100644
index 363e03ea..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
-{
- enum NotificationEventType : uint
- {
- Invalid = 0x0,
- FriendListUpdate = 0x1,
- NewFriendRequest = 0x65,
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs
deleted file mode 100644
index aa58433d..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using Ryujinx.Common.Memory;
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
-{
- [StructLayout(LayoutKind.Sequential, Size = 0x10)]
- struct NotificationInfo
- {
- public NotificationEventType Type;
- private Array4<byte> _padding;
- public long NetworkUserIdPlaceholder;
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs
deleted file mode 100644
index 7902d9c5..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
-{
- [Flags]
- enum FriendServicePermissionLevel
- {
- UserMask = 1,
- ViewerMask = 2,
- ManagerMask = 4,
- SystemMask = 8,
-
- Administrator = -1,
- User = UserMask,
- Viewer = UserMask | ViewerMask,
- Manager = UserMask | ViewerMask | ManagerMask,
- System = UserMask | SystemMask,
- }
-}
diff --git a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs b/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs
deleted file mode 100644
index 9687c547..00000000
--- a/src/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/PlayHistoryRegistrationKey.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-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<byte> Reserved;
- public Array16<byte> Uuid;
- }
-}