aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator
diff options
context:
space:
mode:
authorAc_K <Acoustik666@gmail.com>2019-09-19 02:45:11 +0200
committerjduncanator <1518948+jduncanator@users.noreply.github.com>2019-09-19 10:45:11 +1000
commita0720b5681852f3d786d77bd3793b0359dea321c (patch)
tree9d8f61e540d1d1d827999902dad95e5c0c1e076e /Ryujinx.HLE/HOS/Services/Friend/ServiceCreator
parent4af3101b22e6957d6aa48a2768566d658699f4ed (diff)
Refactoring HOS folder structure (#771)
* Refactoring HOS folder structure Refactoring HOS folder structure: - Added some subfolders when needed (Following structure decided in private). - Added some `Types` folders when needed. - Little cleanup here and there. - Add services placeholders for every HOS services (close #766 and #753). * Remove Types namespaces
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Friend/ServiceCreator')
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs29
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs24
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs10
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs27
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs170
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs175
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs83
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs9
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs15
-rw-r--r--Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs19
12 files changed, 582 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs
new file mode 100644
index 00000000..4947a5ce
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/Friend.cs
@@ -0,0 +1,29 @@
+using Ryujinx.HLE.Utilities;
+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 UInt128 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)]
+ char[] Unknown;
+
+ [MarshalAs(UnmanagedType.I1)]
+ public bool IsValid;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs
new file mode 100644
index 00000000..261bf7bf
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/FriendFilter.cs
@@ -0,0 +1,24 @@
+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;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs
new file mode 100644
index 00000000..df2e6525
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatus.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
+{
+ enum PresenceStatus : uint
+ {
+ Offline,
+ Online,
+ OnlinePlay
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs
new file mode 100644
index 00000000..24da7fd3
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/PresenceStatusFilter.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
+{
+ enum PresenceStatusFilter : uint
+ {
+ None,
+ Online,
+ OnlinePlay,
+ OnlineOrOnlinePlay
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs
new file mode 100644
index 00000000..5fe8bfd7
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/FriendService/Types/UserPresence.cs
@@ -0,0 +1,27 @@
+using Ryujinx.HLE.Utilities;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 0x8, CharSet = CharSet.Ansi)]
+ struct UserPresence
+ {
+ public UInt128 UserId;
+ public long LastTimeOnlineTimestamp;
+ public PresenceStatus Status;
+
+ [MarshalAs(UnmanagedType.I1)]
+ public bool SamePresenceGroupApplication;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3)]
+ public char[] Unknown;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC0)]
+ public char[] AppKeyValueStorage;
+
+ public override string ToString()
+ {
+ return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {AppKeyValueStorage} }}";
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs
new file mode 100644
index 00000000..42b34312
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IDaemonSuspendSessionService.cs
@@ -0,0 +1,12 @@
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
+{
+ class IDaemonSuspendSessionService : IpcService
+ {
+ private FriendServicePermissionLevel PermissionLevel;
+
+ public IDaemonSuspendSessionService(FriendServicePermissionLevel permissionLevel)
+ {
+ PermissionLevel = permissionLevel;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
new file mode 100644
index 00000000..7492c5a7
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/IFriendService.cs
@@ -0,0 +1,170 @@
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Account.Acc;
+using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService;
+using Ryujinx.HLE.Utilities;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
+{
+ class IFriendService : IpcService
+ {
+ private FriendServicePermissionLevel _permissionLevel;
+
+ public IFriendService(FriendServicePermissionLevel permissionLevel)
+ {
+ _permissionLevel = permissionLevel;
+ }
+
+ [Command(10100)]
+ // nn::friends::GetFriendListIds(int offset, nn::account::Uid userUUID, 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();
+
+ UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
+ FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
+
+ // Pid placeholder
+ context.RequestData.ReadInt64();
+
+ if (uuid.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.PrintStub(LogClass.ServiceFriend, new
+ {
+ UserId = uuid.ToString(),
+ offset,
+ filter.PresenceStatus,
+ filter.IsFavoriteOnly,
+ filter.IsSameAppPresenceOnly,
+ filter.IsSameAppPlayedOnly,
+ filter.IsArbitraryAppPlayedOnly,
+ filter.PresenceGroupId,
+ });
+
+ return ResultCode.Success;
+ }
+
+ [Command(10101)]
+ // nn::friends::GetFriendList(int offset, nn::account::Uid userUUID, 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();
+
+ UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
+ FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
+
+ // Pid placeholder
+ context.RequestData.ReadInt64();
+
+ if (uuid.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.PrintStub(LogClass.ServiceFriend, new {
+ UserId = uuid.ToString(),
+ offset,
+ filter.PresenceStatus,
+ filter.IsFavoriteOnly,
+ filter.IsSameAppPresenceOnly,
+ filter.IsSameAppPlayedOnly,
+ filter.IsArbitraryAppPlayedOnly,
+ filter.PresenceGroupId,
+ });
+
+ return ResultCode.Success;
+ }
+
+ [Command(10600)]
+ // nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid)
+ public ResultCode DeclareOpenOnlinePlaySession(ServiceCtx context)
+ {
+ UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
+
+ if (uuid.IsNull)
+ {
+ return ResultCode.InvalidArgument;
+ }
+
+ if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile))
+ {
+ profile.OnlinePlayState = AccountState.Open;
+ }
+
+ Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), profile.OnlinePlayState });
+
+ return ResultCode.Success;
+ }
+
+ [Command(10601)]
+ // nn::friends::DeclareCloseOnlinePlaySession(nn::account::Uid)
+ public ResultCode DeclareCloseOnlinePlaySession(ServiceCtx context)
+ {
+ UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
+
+ if (uuid.IsNull)
+ {
+ return ResultCode.InvalidArgument;
+ }
+
+ if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile))
+ {
+ profile.OnlinePlayState = AccountState.Closed;
+ }
+
+ Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), profile.OnlinePlayState });
+
+ return ResultCode.Success;
+ }
+
+ [Command(10610)]
+ // nn::friends::UpdateUserPresence(nn::account::Uid, u64, pid, buffer<nn::friends::detail::UserPresenceImpl, 0x19>)
+ public ResultCode UpdateUserPresence(ServiceCtx context)
+ {
+ UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
+
+ // Pid placeholder
+ context.RequestData.ReadInt64();
+
+ long position = context.Request.PtrBuff[0].Position;
+ long size = context.Request.PtrBuff[0].Size;
+
+ byte[] bufferContent = context.Memory.ReadBytes(position, size);
+
+ if (uuid.IsNull)
+ {
+ return ResultCode.InvalidArgument;
+ }
+
+ int elementCount = bufferContent.Length / Marshal.SizeOf<UserPresence>();
+
+ using (BinaryReader bufferReader = new BinaryReader(new MemoryStream(bufferContent)))
+ {
+ UserPresence[] userPresenceInputArray = bufferReader.ReadStructArray<UserPresence>(elementCount);
+
+ Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), userPresenceInputArray });
+ }
+
+ return ResultCode.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs
new file mode 100644
index 00000000..1ff37442
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/INotificationService.cs
@@ -0,0 +1,175 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
+{
+ class INotificationService : IpcService, IDisposable
+ {
+ private readonly UInt128 _userId;
+ private readonly FriendServicePermissionLevel _permissionLevel;
+
+ private readonly object _lock = new object();
+
+ private KEvent _notificationEvent;
+ private int _notificationEventHandle = 0;
+
+ private LinkedList<NotificationInfo> _notifications;
+
+ private bool _hasNewFriendRequest;
+ private bool _hasFriendListUpdate;
+
+ public INotificationService(ServiceCtx context, UInt128 userId, FriendServicePermissionLevel permissionLevel)
+ {
+ _userId = userId;
+ _permissionLevel = permissionLevel;
+ _notifications = new LinkedList<NotificationInfo>();
+ _notificationEvent = new KEvent(context.Device.System);
+
+ _hasNewFriendRequest = false;
+ _hasFriendListUpdate = false;
+
+ NotificationEventHandler.Instance.RegisterNotificationService(this);
+ }
+
+ [Command(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) != KernelResult.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_notificationEventHandle);
+
+ return ResultCode.Success;
+ }
+
+ [Command(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;
+ }
+
+ [Command(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(UInt128 targetId)
+ {
+ lock (_lock)
+ {
+ if (_userId == targetId)
+ {
+ if (!_hasFriendListUpdate)
+ {
+ NotificationInfo friendListNotification = new NotificationInfo();
+
+ if (_notifications.Count != 0)
+ {
+ friendListNotification = _notifications.First.Value;
+ _notifications.RemoveFirst();
+ }
+
+ friendListNotification.Type = NotificationEventType.FriendListUpdate;
+ _hasFriendListUpdate = true;
+
+ if (_hasNewFriendRequest)
+ {
+ NotificationInfo newFriendRequestNotification = new NotificationInfo();
+
+ 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(UInt128 targetId)
+ {
+ lock (_lock)
+ {
+ if ((_permissionLevel & FriendServicePermissionLevel.OverlayMask) != 0 && _userId == targetId)
+ {
+ if (!_hasNewFriendRequest)
+ {
+ if (_notifications.Count == 100)
+ {
+ SignalFriendListUpdate(targetId);
+ }
+
+ NotificationInfo newFriendRequestNotification = new NotificationInfo
+ {
+ Type = NotificationEventType.NewFriendRequest
+ };
+
+ _notifications.AddLast(newFriendRequestNotification);
+ _hasNewFriendRequest = true;
+ }
+
+ _notificationEvent.ReadableEvent.Signal();
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ NotificationEventHandler.Instance.UnregisterNotificationService(this);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs
new file mode 100644
index 00000000..19b15416
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/NotificationEventHandler.cs
@@ -0,0 +1,83 @@
+using Ryujinx.HLE.Utilities;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
+{
+ public sealed class NotificationEventHandler
+ {
+ private static NotificationEventHandler instance;
+ private static object instanceLock = new object();
+
+ private INotificationService[] _registry;
+
+ public static NotificationEventHandler Instance
+ {
+ get
+ {
+ lock (instanceLock)
+ {
+ if (instance == null)
+ {
+ 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(UInt128 targetId)
+ {
+ for (int i = 0; i < _registry.Length; i++)
+ {
+ if (_registry[i] != null)
+ {
+ _registry[i].SignalFriendListUpdate(targetId);
+ }
+ }
+ }
+
+ // TODO: Use this when we will have enough things to go online.
+ public void SignalNewFriendRequest(UInt128 targetId)
+ {
+ for (int i = 0; i < _registry.Length; i++)
+ {
+ if (_registry[i] != null)
+ {
+ _registry[i].SignalNewFriendRequest(targetId);
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs
new file mode 100644
index 00000000..5136ae8a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationEventType.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
+{
+ enum NotificationEventType : uint
+ {
+ Invalid = 0x0,
+ FriendListUpdate = 0x1,
+ NewFriendRequest = 0x65
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs
new file mode 100644
index 00000000..1bd6f011
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/NotificationService/Types/NotificationInfo.cs
@@ -0,0 +1,15 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.NotificationService
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 0x8, Size = 0x10)]
+ struct NotificationInfo
+ {
+ public NotificationEventType Type;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4)]
+ public char[] Padding;
+
+ public long NetworkUserIdPlaceholder;
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs
new file mode 100644
index 00000000..9c811365
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Friend/ServiceCreator/Types/FriendServicePermissionLevel.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
+{
+ [Flags]
+ enum FriendServicePermissionLevel
+ {
+ UserMask = 1,
+ OverlayMask = 2,
+ ManagerMask = 4,
+ SystemMask = 8,
+
+ Admin = -1,
+ User = UserMask,
+ Overlay = UserMask | OverlayMask,
+ Manager = UserMask | OverlayMask | ManagerMask,
+ System = UserMask | SystemMask
+ }
+} \ No newline at end of file