Refactor the friend namespace and UInt128
This commit also: - Fix GetFriendsList arguments ordering. - Add GetFriendListIds. - Expose the permission level of the port instance. - InvalidUUID => InvalidArgument
This commit is contained in:
parent
b2b736abc2
commit
83282af8e0
9 changed files with 196 additions and 57 deletions
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Friend
|
||||
{
|
||||
[Flags]
|
||||
enum FriendServicePermissionLevel
|
||||
{
|
||||
Admin = -1,
|
||||
User = 1,
|
||||
Overlay = 3,
|
||||
Manager = 7,
|
||||
System = 9
|
||||
}
|
||||
}
|
|
@ -7,14 +7,18 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
{
|
||||
private Dictionary<int, ServiceProcessRequest> _commands;
|
||||
|
||||
private FriendServicePermissionLevel PermissionLevel;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
public IDaemonSuspendSessionService()
|
||||
public IDaemonSuspendSessionService(FriendServicePermissionLevel permissionLevel)
|
||||
{
|
||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||
{
|
||||
// ...
|
||||
};
|
||||
|
||||
PermissionLevel = permissionLevel;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,12 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Ryujinx.HLE.HOS.ErrorCode;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Friend
|
||||
{
|
||||
|
@ -10,64 +14,108 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
{
|
||||
private Dictionary<int, ServiceProcessRequest> _commands;
|
||||
|
||||
private FriendServicePermissionLevel _permissionLevel;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
public IFriendService()
|
||||
public IFriendService(FriendServicePermissionLevel permissionLevel)
|
||||
{
|
||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||
{
|
||||
{ 10100, GetFriendListIds },
|
||||
{ 10101, GetFriendList },
|
||||
{ 10600, DeclareOpenOnlinePlaySession },
|
||||
{ 10601, DeclareCloseOnlinePlaySession },
|
||||
{ 10610, UpdateUserPresence }
|
||||
};
|
||||
|
||||
_permissionLevel = permissionLevel;
|
||||
}
|
||||
|
||||
// nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds, array<nn::account::NetworkServiceAccountId>
|
||||
public long GetFriendList(ServiceCtx context)
|
||||
// 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 long GetFriendListIds(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = new UInt128(
|
||||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
int offset = context.RequestData.ReadInt32();
|
||||
|
||||
int unknown0 = context.RequestData.ReadInt32();
|
||||
// Padding
|
||||
context.RequestData.ReadInt32();
|
||||
|
||||
FriendFilter filter = new FriendFilter
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
FriendFilter filter = context.RequestData.ReadStruct<FriendFilter>();
|
||||
|
||||
// Pid placeholder
|
||||
context.RequestData.ReadInt64();
|
||||
|
||||
if (uuid.IsNull)
|
||||
{
|
||||
PresenceStatus = (PresenceStatusFilter)context.RequestData.ReadInt32(),
|
||||
IsFavoriteOnly = context.RequestData.ReadBoolean(),
|
||||
IsSameAppPresenceOnly = context.RequestData.ReadBoolean(),
|
||||
IsSameAppPlayedOnly = context.RequestData.ReadBoolean(),
|
||||
IsArbitraryAppPlayedOnly = context.RequestData.ReadBoolean(),
|
||||
PresenceGroupId = context.RequestData.ReadInt64()
|
||||
};
|
||||
|
||||
long unknown1 = context.RequestData.ReadInt64();
|
||||
return MakeError(ErrorModule.Friends, FriendErr.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 {
|
||||
Logger.PrintStub(LogClass.ServiceFriend, new
|
||||
{
|
||||
UserId = uuid.ToString(),
|
||||
unknown0,
|
||||
offset,
|
||||
filter.PresenceStatus,
|
||||
filter.IsFavoriteOnly,
|
||||
filter.IsSameAppPresenceOnly,
|
||||
filter.IsSameAppPlayedOnly,
|
||||
filter.IsArbitraryAppPlayedOnly,
|
||||
filter.PresenceGroupId,
|
||||
unknown1
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// DeclareOpenOnlinePlaySession(nn::account::Uid)
|
||||
// nn::friends::GetFriendList(int offset, nn::account::Uid userUUID, nn::friends::detail::ipc::SizedFriendFilter friendFilter, ulong pidPlaceHolder, pid) -> int outCount, array<nn::account::NetworkServiceAccountId, 0x6>
|
||||
public long 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 MakeError(ErrorModule.Friends, FriendErr.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 0;
|
||||
}
|
||||
|
||||
// nn::friends::DeclareOpenOnlinePlaySession(nn::account::Uid)
|
||||
public long DeclareOpenOnlinePlaySession(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = new UInt128(
|
||||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
if (uuid.IsNull)
|
||||
{
|
||||
return MakeError(ErrorModule.Friends, FriendErr.InvalidArgument);
|
||||
}
|
||||
|
||||
if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile))
|
||||
{
|
||||
|
@ -79,12 +127,15 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
return 0;
|
||||
}
|
||||
|
||||
// DeclareCloseOnlinePlaySession(nn::account::Uid)
|
||||
// nn::friends::DeclareCloseOnlinePlaySession(nn::account::Uid)
|
||||
public long DeclareCloseOnlinePlaySession(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = new UInt128(
|
||||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
if (uuid.IsNull)
|
||||
{
|
||||
return MakeError(ErrorModule.Friends, FriendErr.InvalidArgument);
|
||||
}
|
||||
|
||||
if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile))
|
||||
{
|
||||
|
@ -96,21 +147,31 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
return 0;
|
||||
}
|
||||
|
||||
// UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer<Unknown1, type: 0x19, size: 0xe0>
|
||||
// nn::friends::UpdateUserPresence(nn::account::Uid, u64, pid, buffer<nn::friends::detail::UserPresenceImpl, 0x19>)
|
||||
public long UpdateUserPresence(ServiceCtx context)
|
||||
{
|
||||
UInt128 uuid = new UInt128(
|
||||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
long unknown0 = context.RequestData.ReadInt64();
|
||||
// Pid placeholder
|
||||
context.RequestData.ReadInt64();
|
||||
|
||||
long position = context.Request.PtrBuff[0].Position;
|
||||
long size = context.Request.PtrBuff[0].Size;
|
||||
|
||||
// TODO: Write the buffer content.
|
||||
byte[] bufferContent = context.Memory.ReadBytes(position, size);
|
||||
|
||||
Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), unknown0 });
|
||||
if (uuid.IsNull)
|
||||
{
|
||||
return MakeError(ErrorModule.Friends, FriendErr.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 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
using Ryujinx.HLE.Utilities;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Friend
|
||||
{
|
||||
enum PresenceStatusFilter
|
||||
enum PresenceStatusFilter : uint
|
||||
{
|
||||
None,
|
||||
Online,
|
||||
|
@ -8,13 +11,49 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
OnlineOrOnlinePlay
|
||||
}
|
||||
|
||||
enum PresenceStatus : uint
|
||||
{
|
||||
Offline,
|
||||
Online,
|
||||
OnlinePlay,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct FriendFilter
|
||||
{
|
||||
public PresenceStatusFilter PresenceStatus;
|
||||
public bool IsFavoriteOnly;
|
||||
public bool IsSameAppPresenceOnly;
|
||||
public bool IsSameAppPlayedOnly;
|
||||
public bool IsArbitraryAppPlayedOnly;
|
||||
public long PresenceGroupId;
|
||||
|
||||
[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;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct UserPresence
|
||||
{
|
||||
public UInt128 UserId;
|
||||
public long LastTimeOnlineTimestamp;
|
||||
public PresenceStatus Status;
|
||||
|
||||
uint padding;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xC0)]
|
||||
public char[] AppKeyValueStorage;
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {AppKeyValueStorage} }}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,13 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
private KEvent _notificationEvent;
|
||||
private int _notificationEventHandle = 0;
|
||||
|
||||
private FriendServicePermissionLevel _permissionLevel;
|
||||
|
||||
private Dictionary<int, ServiceProcessRequest> _commands;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
public INotificationService(UInt128 userId)
|
||||
public INotificationService(UInt128 userId, FriendServicePermissionLevel permissionLevel)
|
||||
{
|
||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||
{
|
||||
|
@ -28,6 +30,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
};
|
||||
|
||||
_userId = userId;
|
||||
|
||||
_permissionLevel = permissionLevel;
|
||||
}
|
||||
|
||||
public long GetEvent(ServiceCtx context)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using System.Collections.Generic;
|
||||
|
@ -10,9 +11,11 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
{
|
||||
private Dictionary<int, ServiceProcessRequest> _commands;
|
||||
|
||||
private FriendServicePermissionLevel _permissionLevel;
|
||||
|
||||
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
|
||||
|
||||
public IServiceCreator()
|
||||
public IServiceCreator(FriendServicePermissionLevel permissionLevel)
|
||||
{
|
||||
_commands = new Dictionary<int, ServiceProcessRequest>
|
||||
{
|
||||
|
@ -20,35 +23,37 @@ namespace Ryujinx.HLE.HOS.Services.Friend
|
|||
{ 1, CreateNotificationService }, // 2.0.0+
|
||||
{ 2, CreateDaemonSuspendSessionService }, // 4.0.0+
|
||||
};
|
||||
|
||||
_permissionLevel = permissionLevel;
|
||||
}
|
||||
|
||||
// CreateFriendService() -> object<nn::friends::detail::ipc::IFriendService>
|
||||
public static long CreateFriendService(ServiceCtx context)
|
||||
public long CreateFriendService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IFriendService());
|
||||
MakeObject(context, new IFriendService(_permissionLevel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CreateNotificationService(nn::account::Uid) -> object<nn::friends::detail::ipc::INotificationService>
|
||||
public static long CreateNotificationService(ServiceCtx context)
|
||||
public long CreateNotificationService(ServiceCtx context)
|
||||
{
|
||||
UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10));
|
||||
UInt128 userId = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
if (userId.IsNull)
|
||||
{
|
||||
return MakeError(ErrorModule.Friends, FriendErr.InvalidArgument);
|
||||
}
|
||||
|
||||
MakeObject(context, new INotificationService(userId));
|
||||
MakeObject(context, new INotificationService(userId, _permissionLevel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CreateDaemonSuspendSessionService() -> object<nn::friends::detail::ipc::IDaemonSuspendSessionService>
|
||||
public static long CreateDaemonSuspendSessionService(ServiceCtx context)
|
||||
public long CreateDaemonSuspendSessionService(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IDaemonSuspendSessionService());
|
||||
MakeObject(context, new IDaemonSuspendSessionService(_permissionLevel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using LibHac;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.NcaUtils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
|
@ -234,9 +235,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv
|
|||
|
||||
long titleId = context.RequestData.ReadInt64();
|
||||
|
||||
UInt128 userId = new UInt128(
|
||||
context.RequestData.ReadInt64(),
|
||||
context.RequestData.ReadInt64());
|
||||
UInt128 userId = context.RequestData.ReadStruct<UInt128>();
|
||||
|
||||
long saveId = context.RequestData.ReadInt64();
|
||||
SaveDataType saveDataType = (SaveDataType)context.RequestData.ReadByte();
|
||||
|
|
|
@ -100,10 +100,19 @@ namespace Ryujinx.HLE.HOS.Services
|
|||
return new IeTicketService();
|
||||
|
||||
case "friend:a":
|
||||
return new Friend.IServiceCreator();
|
||||
return new Friend.IServiceCreator(Friend.FriendServicePermissionLevel.Admin);
|
||||
|
||||
case "friend:u":
|
||||
return new Friend.IServiceCreator();
|
||||
return new Friend.IServiceCreator(Friend.FriendServicePermissionLevel.User);
|
||||
|
||||
case "friend:v":
|
||||
return new Friend.IServiceCreator(Friend.FriendServicePermissionLevel.Overlay);
|
||||
|
||||
case "friend:m":
|
||||
return new Friend.IServiceCreator(Friend.FriendServicePermissionLevel.Manager);
|
||||
|
||||
case "friend:s":
|
||||
return new Friend.IServiceCreator(Friend.FriendServicePermissionLevel.System);
|
||||
|
||||
case "fsp-srv":
|
||||
return new IFileSystemProxy();
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.Utilities
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct UInt128
|
||||
{
|
||||
public long High { get; private set; }
|
||||
public long Low { get; private set; }
|
||||
public long High { get; private set; }
|
||||
|
||||
public bool IsNull => (Low | High) == 0;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue