diff --git a/Ryujinx.HLE/HOS/Services/Acc/AccErr.cs b/Ryujinx.HLE/HOS/Services/Acc/AccErr.cs index 144d6680..6d770760 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/AccErr.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/AccErr.cs @@ -2,6 +2,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc { static class AccErr { - public const int UserNotFound = 100; + public const int NullArgument = 20; + public const int InvalidArgument = 22; + public const int NullInputBuffer = 30; + public const int InvalidInputBufferSize = 31; + public const int InvalidInputBuffer = 32; + public const int ApplicationLaunchPropertyAlreadyInit = 41; + public const int UserNotFound = 100; + public const int NullObject = 302; + public const int UnknownError1 = 341; } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/OpenCloseState.cs b/Ryujinx.HLE/HOS/Services/Acc/Account/AccountState.cs similarity index 72% rename from Ryujinx.HLE/HOS/SystemState/OpenCloseState.cs rename to Ryujinx.HLE/HOS/Services/Acc/Account/AccountState.cs index a2678b5c..7e7dd841 100644 --- a/Ryujinx.HLE/HOS/SystemState/OpenCloseState.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/Account/AccountState.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.SystemState { - public enum OpenCloseState + public enum AccountState { Closed, Open diff --git a/Ryujinx.HLE/HOS/Services/Acc/Account/AccountUtils.cs b/Ryujinx.HLE/HOS/Services/Acc/Account/AccountUtils.cs new file mode 100644 index 00000000..5e7f4dac --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Acc/Account/AccountUtils.cs @@ -0,0 +1,68 @@ +using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.HLE.HOS.Services.Acc +{ + public class AccountUtils + { + private ConcurrentDictionary _profiles; + + internal UserProfile LastOpenedUser { get; private set; } + + public AccountUtils() + { + _profiles = new ConcurrentDictionary(); + } + + public void AddUser(UInt128 userId, string name) + { + UserProfile profile = new UserProfile(userId, name); + + _profiles.AddOrUpdate(userId.ToString(), profile, (key, old) => profile); + } + + public void OpenUser(UInt128 userId) + { + if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile)) + { + (LastOpenedUser = profile).AccountState = AccountState.Open; + } + } + + public void CloseUser(UInt128 userId) + { + if (_profiles.TryGetValue(userId.ToString(), out UserProfile profile)) + { + profile.AccountState = AccountState.Closed; + } + } + + public int GetUserCount() + { + return _profiles.Count; + } + + internal bool TryGetUser(UInt128 userId, out UserProfile profile) + { + return _profiles.TryGetValue(userId.ToString(), out profile); + } + + internal IEnumerable GetAllUsers() + { + return _profiles.Values; + } + + internal IEnumerable GetOpenedUsers() + { + return _profiles.Values.Where(x => x.AccountState == AccountState.Open); + } + + internal UserProfile GetFirst() + { + return _profiles.First().Value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs b/Ryujinx.HLE/HOS/Services/Acc/Account/UserProfile.cs similarity index 60% rename from Ryujinx.HLE/HOS/SystemState/UserProfile.cs rename to Ryujinx.HLE/HOS/Services/Acc/Account/UserProfile.cs index 9240389c..179d6925 100644 --- a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/Account/UserProfile.cs @@ -7,24 +7,24 @@ namespace Ryujinx.HLE.HOS.SystemState { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public UInt128 Uuid { get; private set; } + public UInt128 UserId { get; private set; } public string Name { get; private set; } public long LastModifiedTimestamp { get; private set; } - public OpenCloseState AccountState { get; set; } - public OpenCloseState OnlinePlayState { get; set; } + public AccountState AccountState { get; set; } + public AccountState OnlinePlayState { get; set; } - public UserProfile(UInt128 uuid, string name) + public UserProfile(UInt128 userId, string name) { - Uuid = uuid; - Name = name; + UserId = userId; + Name = name; LastModifiedTimestamp = 0; - AccountState = OpenCloseState.Closed; - OnlinePlayState = OpenCloseState.Closed; + AccountState = AccountState.Closed; + OnlinePlayState = AccountState.Closed; UpdateTimestamp(); } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index f6c3cef9..101cb361 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -1,7 +1,10 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Services.Arp; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -10,6 +13,10 @@ namespace Ryujinx.HLE.HOS.Services.Acc { class IAccountService : IpcService { + private bool _userRegistrationRequestPermitted = false; + + private ApplicationLaunchProperty _applicationLaunchProperty; + private Dictionary _commands; public override IReadOnlyDictionary Commands => _commands; @@ -18,23 +25,36 @@ namespace Ryujinx.HLE.HOS.Services.Acc { _commands = new Dictionary { - { 0, GetUserCount }, - { 1, GetUserExistence }, - { 2, ListAllUsers }, - { 3, ListOpenUsers }, - { 4, GetLastOpenedUser }, - { 5, GetProfile }, - { 50, IsUserRegistrationRequestPermitted }, - { 51, TrySelectUserWithoutInteraction }, - { 100, InitializeApplicationInfo }, - { 101, GetBaasAccountManagerForApplication } + { 0, GetUserCount }, + { 1, GetUserExistence }, + { 2, ListAllUsers }, + { 3, ListOpenUsers }, + { 4, GetLastOpenedUser }, + { 5, GetProfile }, + //{ 6, GetProfileDigest }, // 3.0.0+ + { 50, IsUserRegistrationRequestPermitted }, + { 51, TrySelectUserWithoutInteraction }, + //{ 60, ListOpenContextStoredUsers }, // 5.0.0-5.1.0 + //{ 99, DebugActivateOpenContextRetention }, // 6.0.0+ + { 100, InitializeApplicationInfo }, + { 101, GetBaasAccountManagerForApplication }, + //{ 102, AuthenticateApplicationAsync }, + //{ 103, CheckNetworkServiceAvailabilityAsync }, // 4.0.0+ + { 110, StoreSaveDataThumbnail }, + { 111, ClearSaveDataThumbnail }, + //{ 120, CreateGuestLoginRequest }, + //{ 130, LoadOpenContext }, // 6.0.0+ + //{ 131, ListOpenContextStoredUsers }, // 6.0.0+ + { 140, InitializeApplicationInfo }, // 6.0.0+ + //{ 141, ListQualifiedUsers }, // 6.0.0+ + { 150, IsUserAccountSwitchLocked }, // 6.0.0+ }; } // GetUserCount() -> i32 public long GetUserCount(ServiceCtx context) { - context.ResponseData.Write(context.Device.System.State.GetUserCount()); + context.ResponseData.Write(context.Device.System.State.Account.GetUserCount()); return 0; } @@ -42,11 +62,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc // GetUserExistence(nn::account::Uid) -> bool public long GetUserExistence(ServiceCtx context) { - UInt128 uuid = new UInt128( - context.RequestData.ReadInt64(), - context.RequestData.ReadInt64()); + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); - context.ResponseData.Write(context.Device.System.State.TryGetUser(uuid, out _)); + if (userId.IsNull) + { + return MakeError(ErrorModule.Account, AccErr.NullArgument); + } + + context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _)); return 0; } @@ -54,31 +77,38 @@ namespace Ryujinx.HLE.HOS.Services.Acc // ListAllUsers() -> array public long ListAllUsers(ServiceCtx context) { - return WriteUserList(context, context.Device.System.State.GetAllUsers()); + return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); } // ListOpenUsers() -> array public long ListOpenUsers(ServiceCtx context) { - return WriteUserList(context, context.Device.System.State.GetOpenUsers()); + return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers()); } private long WriteUserList(ServiceCtx context, IEnumerable profiles) { + if (context.Request.RecvListBuff.Count == 0) + { + return MakeError(ErrorModule.Account, AccErr.InvalidInputBuffer); + } + long outputPosition = context.Request.RecvListBuff[0].Position; long outputSize = context.Request.RecvListBuff[0].Size; - long offset = 0; + ulong offset = 0; - foreach (UserProfile profile in profiles) + foreach (UserProfile userProfile in profiles) { - if ((ulong)offset + 16 > (ulong)outputSize) + if (offset + 0x10 > (ulong)outputSize) { break; } - context.Memory.WriteInt64(outputPosition, profile.Uuid.Low); - context.Memory.WriteInt64(outputPosition + 8, profile.Uuid.High); + context.Memory.WriteInt64(outputPosition + (long)offset, userProfile.UserId.Low); + context.Memory.WriteInt64(outputPosition + (long)offset + 8, userProfile.UserId.High); + + offset += 0x10; } return 0; @@ -87,9 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc // GetLastOpenedUser() -> nn::account::Uid public long GetLastOpenedUser(ServiceCtx context) { - UserProfile lastOpened = context.Device.System.State.LastOpenUser; - - lastOpened.Uuid.Write(context.ResponseData); + context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData); return 0; } @@ -97,18 +125,19 @@ namespace Ryujinx.HLE.HOS.Services.Acc // GetProfile(nn::account::Uid) -> object public long GetProfile(ServiceCtx context) { - UInt128 uuid = new UInt128( - context.RequestData.ReadInt64(), - context.RequestData.ReadInt64()); + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); - if (!context.Device.System.State.TryGetUser(uuid, out UserProfile profile)) + if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile)) { - Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{uuid} not found!"); + Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{userId} not found!"); return MakeError(ErrorModule.Account, AccErr.UserNotFound); } - MakeObject(context, new IProfile(profile)); + MakeObject(context, new IProfile(userProfile)); + + // Doesn't occur in our case. + // return MakeError(ErrorModule.Account, AccErr.NullObject); return 0; } @@ -116,11 +145,8 @@ namespace Ryujinx.HLE.HOS.Services.Acc // IsUserRegistrationRequestPermitted(u64, pid) -> bool public long IsUserRegistrationRequestPermitted(ServiceCtx context) { - long unknown = context.RequestData.ReadInt64(); - - Logger.PrintStub(LogClass.ServiceAcc, new { unknown }); - - context.ResponseData.Write(false); + // The u64 argument seems to be unused by account. + context.ResponseData.Write(_userRegistrationRequestPermitted); return 0; } @@ -128,35 +154,175 @@ namespace Ryujinx.HLE.HOS.Services.Acc // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid public long TrySelectUserWithoutInteraction(ServiceCtx context) { - bool unknown = context.RequestData.ReadBoolean(); + if (context.Device.System.State.Account.GetUserCount() != 1) + { + // Invalid UserId. + new UInt128(0, 0).Write(context.ResponseData); - Logger.PrintStub(LogClass.ServiceAcc, new { unknown }); + return 0; + } - UserProfile profile = context.Device.System.State.LastOpenUser; + bool baasCheck = context.RequestData.ReadBoolean(); - profile.Uuid.Write(context.ResponseData); + if (baasCheck) + { + // This checks something related to baas (online), and then return an invalid UserId if the check in baas returns an error code. + // In our case, we can just log it for now. + + Logger.PrintStub(LogClass.ServiceAcc, new { baasCheck }); + } + + // As we returned an invalid UserId if there is more than one user earlier, now we can return only the first one. + context.Device.System.State.Account.GetFirst().UserId.Write(context.ResponseData); return 0; } // InitializeApplicationInfo(u64, pid) + // Both calls (100, 140) use the same submethod, maybe there's something different further along when arp:r is called? public long InitializeApplicationInfo(ServiceCtx context) { + if (_applicationLaunchProperty != null) + { + return MakeError(ErrorModule.Account, AccErr.ApplicationLaunchPropertyAlreadyInit); + } + + // The u64 argument seems to be unused by account. long unknown = context.RequestData.ReadInt64(); + // TODO: Account actually calls nn::arp::detail::IReader::GetApplicationLaunchProperty() with the current PID and store the result (ApplicationLaunchProperty) internally. + // For now we can hardcode values, and fix it after GetApplicationLaunchProperty is implemented. + + /* + if (nn::arp::detail::IReader::GetApplicationLaunchProperty() == 0xCC9D) // InvalidProcessId + { + _applicationLaunchProperty = new ApplicationLaunchProperty + { + TitleId = 0x00; + Version = 0x00; + BaseGameStorageId = 0x03; + UpdateGameStorageId = 0x00; + } + + return MakeError(ErrorModule.Account, AccErr.InvalidArgument); + } + else + */ + { + _applicationLaunchProperty = new ApplicationLaunchProperty + { + TitleId = BitConverter.ToInt64(StringUtils.HexToBytes(context.Device.System.TitleID), 0), + Version = 0x00, + BaseGameStorageId = (byte)StorageId.NandSystem, + UpdateGameStorageId = (byte)StorageId.None + }; + } + Logger.PrintStub(LogClass.ServiceAcc, new { unknown }); return 0; } - // GetBaasAccountManagerForApplication(nn::account::Uid) -> object + // GetBaasAccountManagerForApplication(nn::account::Uid) -> object public long GetBaasAccountManagerForApplication(ServiceCtx context) { - UInt128 uuid = new UInt128( - context.RequestData.ReadInt64(), - context.RequestData.ReadInt64()); + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); - MakeObject(context, new IManagerForApplication(uuid)); + if (userId.IsNull) + { + return MakeError(ErrorModule.Account, AccErr.NullArgument); + } + + if (_applicationLaunchProperty == null) + { + return MakeError(ErrorModule.Account, AccErr.InvalidArgument); + } + + MakeObject(context, new IManagerForApplication(userId, _applicationLaunchProperty)); + + // Doesn't occur in our case. + // return MakeError(ErrorModule.Account, AccErr.NullObject); + + return 0; + } + + // StoreSaveDataThumbnail(nn::account::Uid, buffer) + public long StoreSaveDataThumbnail(ServiceCtx context) + { + if (_applicationLaunchProperty == null) + { + return MakeError(ErrorModule.Account, AccErr.InvalidArgument); + } + + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return MakeError(ErrorModule.Account, AccErr.NullArgument); + } + + if (context.Request.SendBuff.Count == 0) + { + return MakeError(ErrorModule.Account, AccErr.InvalidInputBuffer); + } + + long inputPosition = context.Request.SendBuff[0].Position; + long inputSize = context.Request.SendBuff[0].Size; + + if (inputSize != 0x24000) + { + return MakeError(ErrorModule.Account, AccErr.InvalidInputBufferSize); + } + + byte[] thumbnailBuffer = context.Memory.ReadBytes(inputPosition, inputSize); + + // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ? + + Logger.PrintStub(LogClass.ServiceAcc); + + return 0; + } + + // ClearSaveDataThumbnail(nn::account::Uid) + public long ClearSaveDataThumbnail(ServiceCtx context) + { + if (_applicationLaunchProperty == null) + { + return MakeError(ErrorModule.Account, AccErr.InvalidArgument); + } + + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return MakeError(ErrorModule.Account, AccErr.NullArgument); + } + + // TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ? + + Logger.PrintStub(LogClass.ServiceAcc); + + return 0; + } + + // IsUserAccountSwitchLocked() -> bool + public long IsUserAccountSwitchLocked(ServiceCtx context) + { + // TODO : Validate the following check. + /* + if (_applicationLaunchProperty != null) + { + return MakeError(ErrorModule.Account, AccErr.ApplicationLaunchPropertyAlreadyInit); + } + */ + + // Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current PID and store the result (NACP File) internally. + // But since we use LibHac and we load one Application at a time, it's not necessary. + + // TODO : Use "context.Device.System.ControlData.UserAccountSwitchLock" when LibHac is updated. + context.ResponseData.Write(false); + + Logger.PrintStub(LogClass.ServiceAcc); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs index ba31bf4a..1ac18c5e 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Services.Arp; using Ryujinx.HLE.Utilities; using System.Collections.Generic; @@ -7,13 +8,15 @@ namespace Ryujinx.HLE.HOS.Services.Acc { class IManagerForApplication : IpcService { - private UInt128 _uuid; + private UInt128 _userId; + + private ApplicationLaunchProperty _applicationLaunchProperty; private Dictionary _commands; public override IReadOnlyDictionary Commands => _commands; - public IManagerForApplication(UInt128 uuid) + public IManagerForApplication(UInt128 userId, ApplicationLaunchProperty applicationLaunchProperty) { _commands = new Dictionary { @@ -21,7 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Acc { 1, GetAccountId } }; - _uuid = uuid; + _userId = userId; + _applicationLaunchProperty = applicationLaunchProperty; } // CheckAvailability() diff --git a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs index 18ac53bd..966fda24 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc public long GetBase(ServiceCtx context) { - _profile.Uuid.Write(context.ResponseData); + _profile.UserId.Write(context.ResponseData); context.ResponseData.Write(_profile.LastModifiedTimestamp); diff --git a/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs new file mode 100644 index 00000000..16ffea0b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Arp +{ + class ApplicationLaunchProperty + { + public long TitleId; + public int Version; + public byte BaseGameStorageId; + public byte UpdateGameStorageId; + public short Padding; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs index 1a60a78b..17a32b00 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs @@ -69,9 +69,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend context.RequestData.ReadInt64(), context.RequestData.ReadInt64()); - if (context.Device.System.State.TryGetUser(uuid, out UserProfile profile)) + if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile)) { - profile.OnlinePlayState = OpenCloseState.Open; + profile.OnlinePlayState = AccountState.Open; } Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), profile.OnlinePlayState }); @@ -86,9 +86,9 @@ namespace Ryujinx.HLE.HOS.Services.Friend context.RequestData.ReadInt64(), context.RequestData.ReadInt64()); - if (context.Device.System.State.TryGetUser(uuid, out UserProfile profile)) + if (context.Device.System.State.Account.TryGetUser(uuid, out UserProfile profile)) { - profile.OnlinePlayState = OpenCloseState.Closed; + profile.OnlinePlayState = AccountState.Closed; } Logger.PrintStub(LogClass.ServiceFriend, new { UserId = uuid.ToString(), profile.OnlinePlayState }); diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 436897ed..2f0c35f4 100644 --- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -1,8 +1,6 @@ +using Ryujinx.HLE.HOS.Services.Acc; using Ryujinx.HLE.Utilities; using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; namespace Ryujinx.HLE.HOS.SystemState { @@ -50,21 +48,18 @@ namespace Ryujinx.HLE.HOS.SystemState public bool InstallContents { get; set; } - private ConcurrentDictionary _profiles; - - internal UserProfile LastOpenUser { get; private set; } + public AccountUtils Account { get; private set; } public SystemStateMgr() { SetAudioOutputAsBuiltInSpeaker(); - _profiles = new ConcurrentDictionary(); + Account = new AccountUtils(); - UInt128 defaultUuid = new UInt128("00000000000000000000000000000001"); + UInt128 defaultUid = new UInt128("00000000000000000000000000000001"); - AddUser(defaultUuid, "Player"); - - OpenUser(defaultUuid); + Account.AddUser(defaultUid, "Player"); + Account.OpenUser(defaultUid); } public void SetLanguage(SystemLanguage language) @@ -102,49 +97,6 @@ namespace Ryujinx.HLE.HOS.SystemState ActiveAudioOutput = AudioOutputs[2]; } - public void AddUser(UInt128 uuid, string name) - { - UserProfile profile = new UserProfile(uuid, name); - - _profiles.AddOrUpdate(uuid.ToString(), profile, (key, old) => profile); - } - - public void OpenUser(UInt128 uuid) - { - if (_profiles.TryGetValue(uuid.ToString(), out UserProfile profile)) - { - (LastOpenUser = profile).AccountState = OpenCloseState.Open; - } - } - - public void CloseUser(UInt128 uuid) - { - if (_profiles.TryGetValue(uuid.ToString(), out UserProfile profile)) - { - profile.AccountState = OpenCloseState.Closed; - } - } - - public int GetUserCount() - { - return _profiles.Count; - } - - internal bool TryGetUser(UInt128 uuid, out UserProfile profile) - { - return _profiles.TryGetValue(uuid.ToString(), out profile); - } - - internal IEnumerable GetAllUsers() - { - return _profiles.Values; - } - - internal IEnumerable GetOpenUsers() - { - return _profiles.Values.Where(x => x.AccountState == OpenCloseState.Open); - } - internal static long GetLanguageCode(int index) { if ((uint)index >= LanguageCodes.Length) diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs index aa348e7e..8f5fc28f 100644 --- a/Ryujinx.HLE/Utilities/UInt128.cs +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -9,12 +9,20 @@ namespace Ryujinx.HLE.Utilities public long High { get; private set; } public long Low { get; private set; } + public bool IsNull => (Low | High) == 0; + public UInt128(long low, long high) { Low = low; High = high; } + public UInt128(byte[] bytes) + { + Low = BitConverter.ToInt64(bytes, 0); + High = BitConverter.ToInt64(bytes, 8); + } + public UInt128(string hex) { if (hex == null || hex.Length != 32 || !hex.All("0123456789abcdefABCDEF".Contains)) @@ -42,4 +50,4 @@ namespace Ryujinx.HLE.Utilities return (Low | High) == 0; } } -} +} \ No newline at end of file