diff options
| author | Ac_K <Acoustik666@gmail.com> | 2019-09-19 02:45:11 +0200 |
|---|---|---|
| committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-09-19 10:45:11 +1000 |
| commit | a0720b5681852f3d786d77bd3793b0359dea321c (patch) | |
| tree | 9d8f61e540d1d1d827999902dad95e5c0c1e076e /Ryujinx.HLE/HOS/Services/Account | |
| parent | 4af3101b22e6957d6aa48a2768566d658699f4ed (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/Account')
11 files changed, 578 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/AccountUtils.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/AccountUtils.cs new file mode 100644 index 00000000..7a70025a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/AccountUtils.cs @@ -0,0 +1,67 @@ +using Ryujinx.HLE.Utilities; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + public class AccountUtils + { + private ConcurrentDictionary<string, UserProfile> _profiles; + + internal UserProfile LastOpenedUser { get; private set; } + + public AccountUtils() + { + _profiles = new ConcurrentDictionary<string, UserProfile>(); + } + + 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<UserProfile> GetAllUsers() + { + return _profiles.Values; + } + + internal IEnumerable<UserProfile> 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/Services/Account/Acc/IAccountServiceForAdministrator.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs new file mode 100644 index 00000000..9fb3fb9b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [Service("acc:su")] + class IAccountServiceForAdministrator : IpcService + { + public IAccountServiceForAdministrator(ServiceCtx context) { } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs new file mode 100644 index 00000000..84239539 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -0,0 +1,294 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Arp; +using Ryujinx.HLE.Utilities; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [Service("acc:u0")] + class IAccountServiceForApplication : IpcService + { + private bool _userRegistrationRequestPermitted = false; + + private ApplicationLaunchProperty _applicationLaunchProperty; + + public IAccountServiceForApplication(ServiceCtx context) { } + + [Command(0)] + // GetUserCount() -> i32 + public ResultCode GetUserCount(ServiceCtx context) + { + context.ResponseData.Write(context.Device.System.State.Account.GetUserCount()); + + return ResultCode.Success; + } + + [Command(1)] + // GetUserExistence(nn::account::Uid) -> bool + public ResultCode GetUserExistence(ServiceCtx context) + { + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _)); + + return ResultCode.Success; + } + + [Command(2)] + // ListAllUsers() -> array<nn::account::Uid, 0xa> + public ResultCode ListAllUsers(ServiceCtx context) + { + return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); + } + + [Command(3)] + // ListOpenUsers() -> array<nn::account::Uid, 0xa> + public ResultCode ListOpenUsers(ServiceCtx context) + { + return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers()); + } + + private ResultCode WriteUserList(ServiceCtx context, IEnumerable<UserProfile> profiles) + { + if (context.Request.RecvListBuff.Count == 0) + { + return ResultCode.InvalidInputBuffer; + } + + long outputPosition = context.Request.RecvListBuff[0].Position; + long outputSize = context.Request.RecvListBuff[0].Size; + + ulong offset = 0; + + foreach (UserProfile userProfile in profiles) + { + if (offset + 0x10 > (ulong)outputSize) + { + break; + } + + context.Memory.WriteInt64(outputPosition + (long)offset, userProfile.UserId.Low); + context.Memory.WriteInt64(outputPosition + (long)offset + 8, userProfile.UserId.High); + + offset += 0x10; + } + + return ResultCode.Success; + } + + [Command(4)] + // GetLastOpenedUser() -> nn::account::Uid + public ResultCode GetLastOpenedUser(ServiceCtx context) + { + context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData); + + return ResultCode.Success; + } + + [Command(5)] + // GetProfile(nn::account::Uid) -> object<nn::account::profile::IProfile> + public ResultCode GetProfile(ServiceCtx context) + { + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile)) + { + Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{userId} not found!"); + + return ResultCode.UserNotFound; + } + + MakeObject(context, new IProfile(userProfile)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + [Command(50)] + // IsUserRegistrationRequestPermitted(u64, pid) -> bool + public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) + { + // The u64 argument seems to be unused by account. + context.ResponseData.Write(_userRegistrationRequestPermitted); + + return ResultCode.Success; + } + + [Command(51)] + // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid + public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) + { + if (context.Device.System.State.Account.GetUserCount() != 1) + { + // Invalid UserId. + new UInt128(0, 0).Write(context.ResponseData); + + return 0; + } + + bool baasCheck = context.RequestData.ReadBoolean(); + + 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 ResultCode.Success; + } + + [Command(100)] + [Command(140)] // 6.0.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 ResultCode InitializeApplicationInfo(ServiceCtx context) + { + if (_applicationLaunchProperty != null) + { + return ResultCode.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 = ApplicationLaunchProperty.Default; + + return ResultCode.InvalidArgument; + } + else + */ + { + _applicationLaunchProperty = ApplicationLaunchProperty.GetByPid(context); + } + + Logger.PrintStub(LogClass.ServiceAcc, new { unknown }); + + return ResultCode.Success; + } + + [Command(101)] + // GetBaasAccountManagerForApplication(nn::account::Uid) -> object<nn::account::baas::IManagerForApplication> + public ResultCode GetBaasAccountManagerForApplication(ServiceCtx context) + { + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + if (_applicationLaunchProperty == null) + { + return ResultCode.InvalidArgument; + } + + MakeObject(context, new IManagerForApplication(userId, _applicationLaunchProperty)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + [Command(110)] + // StoreSaveDataThumbnail(nn::account::Uid, buffer<bytes, 5>) + public ResultCode StoreSaveDataThumbnail(ServiceCtx context) + { + if (_applicationLaunchProperty == null) + { + return ResultCode.InvalidArgument; + } + + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + if (context.Request.SendBuff.Count == 0) + { + return ResultCode.InvalidInputBuffer; + } + + long inputPosition = context.Request.SendBuff[0].Position; + long inputSize = context.Request.SendBuff[0].Size; + + if (inputSize != 0x24000) + { + return ResultCode.InvalidInputBufferSize; + } + + byte[] thumbnailBuffer = context.Memory.ReadBytes(inputPosition, inputSize); + + // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ? + + Logger.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + [Command(111)] + // ClearSaveDataThumbnail(nn::account::Uid) + public ResultCode ClearSaveDataThumbnail(ServiceCtx context) + { + if (_applicationLaunchProperty == null) + { + return ResultCode.InvalidArgument; + } + + UInt128 userId = new UInt128(context.RequestData.ReadBytes(0x10)); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + // TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ? + + Logger.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + [Command(150)] // 6.0.0+ + // IsUserAccountSwitchLocked() -> bool + public ResultCode IsUserAccountSwitchLocked(ServiceCtx context) + { + // TODO : Validate the following check. + /* + if (_applicationLaunchProperty != null) + { + return ResultCode.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. + + context.ResponseData.Write(context.Device.System.ControlData.UserAccountSwitchLock); + + Logger.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs new file mode 100644 index 00000000..f1972f63 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [Service("acc:u1")] + class IAccountServiceForSystemService : IpcService + { + public IAccountServiceForSystemService(ServiceCtx context) { } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs new file mode 100644 index 00000000..d28ea275 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + [Service("acc:aa")] + class IBaasAccessTokenAccessor : IpcService + { + public IBaasAccessTokenAccessor(ServiceCtx context) { } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs new file mode 100644 index 00000000..aa9e07bd --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs @@ -0,0 +1,40 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Arp; +using Ryujinx.HLE.Utilities; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class IManagerForApplication : IpcService + { + private UInt128 _userId; + private ApplicationLaunchProperty _applicationLaunchProperty; + + public IManagerForApplication(UInt128 userId, ApplicationLaunchProperty applicationLaunchProperty) + { + _userId = userId; + _applicationLaunchProperty = applicationLaunchProperty; + } + + [Command(0)] + // CheckAvailability() + public ResultCode CheckAvailability(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + [Command(1)] + // GetAccountId() -> nn::account::NetworkServiceAccountId + public ResultCode GetAccountId(ServiceCtx context) + { + long networkServiceAccountId = 0xcafe; + + Logger.PrintStub(LogClass.ServiceAcc, new { networkServiceAccountId }); + + context.ResponseData.Write(networkServiceAccountId); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs new file mode 100644 index 00000000..0470832b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs @@ -0,0 +1,80 @@ +using ARMeilleure.Memory; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.Utilities; +using System.IO; +using System.Reflection; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class IProfile : IpcService + { + private UserProfile _profile; + private Stream _profilePictureStream; + + public IProfile(UserProfile profile) + { + _profile = profile; + _profilePictureStream = Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.RyujinxProfileImage.jpg"); + } + + [Command(0)] + // Get() -> (nn::account::profile::ProfileBase, buffer<nn::account::profile::UserData, 0x1a>) + public ResultCode Get(ServiceCtx context) + { + Logger.PrintStub(LogClass.ServiceAcc); + + long position = context.Request.ReceiveBuff[0].Position; + + MemoryHelper.FillWithZeros(context.Memory, position, 0x80); + + context.Memory.WriteInt32(position, 0); + context.Memory.WriteInt32(position + 4, 1); + context.Memory.WriteInt64(position + 8, 1); + + return GetBase(context); + } + + [Command(1)] + // GetBase() -> nn::account::profile::ProfileBase + public ResultCode GetBase(ServiceCtx context) + { + _profile.UserId.Write(context.ResponseData); + + context.ResponseData.Write(_profile.LastModifiedTimestamp); + + byte[] username = StringUtils.GetFixedLengthBytes(_profile.Name, 0x20, Encoding.UTF8); + + context.ResponseData.Write(username); + + return ResultCode.Success; + } + + [Command(10)] + // GetImageSize() -> u32 + public ResultCode GetImageSize(ServiceCtx context) + { + context.ResponseData.Write(_profilePictureStream.Length); + + return ResultCode.Success; + } + + [Command(11)] + // LoadImage() -> (u32, buffer<bytes, 6>) + public ResultCode LoadImage(ServiceCtx context) + { + long bufferPosition = context.Request.ReceiveBuff[0].Position; + long bufferLen = context.Request.ReceiveBuff[0].Size; + + byte[] profilePictureData = new byte[bufferLen]; + + _profilePictureStream.Read(profilePictureData, 0, profilePictureData.Length); + + context.Memory.WriteBytes(bufferPosition, profilePictureData); + + context.ResponseData.Write(_profilePictureStream.Length); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs new file mode 100644 index 00000000..2382a255 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + public enum AccountState + { + Closed, + Open + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs new file mode 100644 index 00000000..25004c24 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserProfile.cs @@ -0,0 +1,37 @@ +using Ryujinx.HLE.Utilities; +using System; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class UserProfile + { + private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + public UInt128 UserId { get; private set; } + + public string Name { get; private set; } + + public long LastModifiedTimestamp { get; private set; } + + public AccountState AccountState { get; set; } + public AccountState OnlinePlayState { get; set; } + + public UserProfile(UInt128 userId, string name) + { + UserId = userId; + Name = name; + + LastModifiedTimestamp = 0; + + AccountState = AccountState.Closed; + OnlinePlayState = AccountState.Closed; + + UpdateTimestamp(); + } + + private void UpdateTimestamp() + { + LastModifiedTimestamp = (long)(DateTime.Now - Epoch).TotalSeconds; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs b/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs new file mode 100644 index 00000000..72301349 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Dauth/IService.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Dauth +{ + [Service("dauth:0")] // 5.0.0+ + class IService : IpcService + { + public IService(ServiceCtx context) { } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs new file mode 100644 index 00000000..e56732ab --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.HLE.HOS.Services.Account +{ + enum ResultCode + { + ModuleId = 124, + ErrorCodeShift = 9, + + Success = 0, + + NullArgument = (20 << ErrorCodeShift) | ModuleId, + InvalidArgument = (22 << ErrorCodeShift) | ModuleId, + NullInputBuffer = (30 << ErrorCodeShift) | ModuleId, + InvalidInputBufferSize = (31 << ErrorCodeShift) | ModuleId, + InvalidInputBuffer = (32 << ErrorCodeShift) | ModuleId, + ApplicationLaunchPropertyAlreadyInit = (41 << ErrorCodeShift) | ModuleId, + UserNotFound = (100 << ErrorCodeShift) | ModuleId, + NullObject = (302 << ErrorCodeShift) | ModuleId, + UnknownError1 = (341 << ErrorCodeShift) | ModuleId + } +}
\ No newline at end of file |
