diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs new file mode 100644 index 0000000000..4631815b5b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForApplication.cs @@ -0,0 +1,61 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class IManagerForApplication : IpcService + { + private ManagerServer _managerServer; + + public IManagerForApplication(UserId userId) + { + _managerServer = new ManagerServer(userId); + } + + [Command(0)] + // CheckAvailability() + public ResultCode CheckAvailability(ServiceCtx context) + { + return _managerServer.CheckAvailability(context); + } + + [Command(1)] + // GetAccountId() -> nn::account::NetworkServiceAccountId + public ResultCode GetAccountId(ServiceCtx context) + { + return _managerServer.GetAccountId(context); + } + + [Command(2)] + // EnsureIdTokenCacheAsync() -> object + public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) + { + ResultCode resultCode = _managerServer.EnsureIdTokenCacheAsync(context, out IAsyncContext asyncContext); + + if (resultCode == ResultCode.Success) + { + MakeObject(context, asyncContext); + } + + return resultCode; + } + + [Command(3)] + // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) + public ResultCode LoadIdTokenCache(ServiceCtx context) + { + return _managerServer.LoadIdTokenCache(context); + } + + [Command(130)] + // GetNintendoAccountUserResourceCacheForApplication() -> (nn::account::NintendoAccountId, nn::account::nas::NasUserBaseForApplication, buffer) + public ResultCode GetNintendoAccountUserResourceCacheForApplication(ServiceCtx context) + { + return _managerServer.GetNintendoAccountUserResourceCacheForApplication(context); + } + + [Command(160)] // 5.0.0+ + // StoreOpenContext() + public ResultCode StoreOpenContext(ServiceCtx context) + { + return _managerServer.StoreOpenContext(context); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs new file mode 100644 index 0000000000..57a466666c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IManagerForSystemService.cs @@ -0,0 +1,47 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class IManagerForSystemService : IpcService + { + private ManagerServer _managerServer; + + public IManagerForSystemService(UserId userId) + { + _managerServer = new ManagerServer(userId); + } + + [Command(0)] + // CheckAvailability() + public ResultCode CheckAvailability(ServiceCtx context) + { + return _managerServer.CheckAvailability(context); + } + + [Command(1)] + // GetAccountId() -> nn::account::NetworkServiceAccountId + public ResultCode GetAccountId(ServiceCtx context) + { + return _managerServer.GetAccountId(context); + } + + [Command(2)] + // EnsureIdTokenCacheAsync() -> object + public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) + { + ResultCode resultCode = _managerServer.EnsureIdTokenCacheAsync(context, out IAsyncContext asyncContext); + + if (resultCode == ResultCode.Success) + { + MakeObject(context, asyncContext); + } + + return resultCode; + } + + [Command(3)] + // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) + public ResultCode LoadIdTokenCache(ServiceCtx context) + { + return _managerServer.LoadIdTokenCache(context); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs new file mode 100644 index 0000000000..c07d90ad35 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfile.cs @@ -0,0 +1,40 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class IProfile : IpcService + { + private ProfileServer _profileServer; + + public IProfile(UserProfile profile) + { + _profileServer = new ProfileServer(profile); + } + + [Command(0)] + // Get() -> (nn::account::profile::ProfileBase, buffer) + public ResultCode Get(ServiceCtx context) + { + return _profileServer.Get(context); + } + + [Command(1)] + // GetBase() -> nn::account::profile::ProfileBase + public ResultCode GetBase(ServiceCtx context) + { + return _profileServer.GetBase(context); + } + + [Command(10)] + // GetImageSize() -> u32 + public ResultCode GetImageSize(ServiceCtx context) + { + return _profileServer.GetImageSize(context); + } + + [Command(11)] + // LoadImage() -> (u32, buffer) + public ResultCode LoadImage(ServiceCtx context) + { + return _profileServer.LoadImage(context); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs new file mode 100644 index 0000000000..33fef3e106 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/IProfileEditor.cs @@ -0,0 +1,54 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class IProfileEditor : IpcService + { + private ProfileServer _profileServer; + + public IProfileEditor(UserProfile profile) + { + _profileServer = new ProfileServer(profile); + } + + [Command(0)] + // Get() -> (nn::account::profile::ProfileBase, buffer) + public ResultCode Get(ServiceCtx context) + { + return _profileServer.Get(context); + } + + [Command(1)] + // GetBase() -> nn::account::profile::ProfileBase + public ResultCode GetBase(ServiceCtx context) + { + return _profileServer.GetBase(context); + } + + [Command(10)] + // GetImageSize() -> u32 + public ResultCode GetImageSize(ServiceCtx context) + { + return _profileServer.GetImageSize(context); + } + + [Command(11)] + // LoadImage() -> (u32, buffer) + public ResultCode LoadImage(ServiceCtx context) + { + return _profileServer.LoadImage(context); + } + + [Command(100)] + // Store(nn::account::profile::ProfileBase, buffer) + public ResultCode Store(ServiceCtx context) + { + return _profileServer.Store(context); + } + + [Command(101)] + // StoreWithImage(nn::account::profile::ProfileBase, buffer, buffer) + public ResultCode StoreWithImage(ServiceCtx context) + { + return _profileServer.StoreWithImage(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/AccountService/ManagerServer.cs similarity index 78% rename from Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs rename to Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs index 746f8f4699..c7efe7789f 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ManagerServer.cs @@ -1,31 +1,24 @@ -using Ryujinx.Common.Logging; +using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext; -using Ryujinx.HLE.HOS.Services.Arp; -using System; -using System.Text; using System.Threading; using System.Threading.Tasks; -namespace Ryujinx.HLE.HOS.Services.Account.Acc +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService { - class IManagerForApplication : IpcService + class ManagerServer { // TODO: Determine where and how NetworkServiceAccountId is set. private const long NetworkServiceAccountId = 0xcafe; - private UserId _userId; - private ApplicationLaunchProperty _applicationLaunchProperty; + private UserId _userId; - public IManagerForApplication(UserId userId, ApplicationLaunchProperty applicationLaunchProperty) + public ManagerServer(UserId userId) { - _userId = userId; - _applicationLaunchProperty = applicationLaunchProperty; + _userId = userId; } - [Command(0)] - // CheckAvailability() public ResultCode CheckAvailability(ServiceCtx context) { // NOTE: This opens the file at "su/baas/USERID_IN_UUID_STRING.dat" where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x". @@ -37,8 +30,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [Command(1)] - // GetAccountId() -> nn::account::NetworkServiceAccountId public ResultCode GetAccountId(ServiceCtx context) { // NOTE: This opens the file at "su/baas/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted @@ -52,16 +43,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [Command(2)] - // EnsureIdTokenCacheAsync() -> object - public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) + public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context, out IAsyncContext asyncContext) { KEvent asyncEvent = new KEvent(context.Device.System.KernelContext); AsyncExecution asyncExecution = new AsyncExecution(asyncEvent); asyncExecution.Initialize(1000, EnsureIdTokenCacheAsyncImpl); - MakeObject(context, new IAsyncContext(asyncExecution)); + asyncContext = new IAsyncContext(asyncExecution); // return ResultCode.NullObject if the IAsyncContext pointer is null. Doesn't occur in our case. @@ -82,8 +71,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc await Task.CompletedTask; } - [Command(3)] - // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) public ResultCode LoadIdTokenCache(ServiceCtx context) { long bufferPosition = context.Request.ReceiveBuff[0].Position; @@ -114,8 +101,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [Command(130)] - // GetNintendoAccountUserResourceCacheForApplication() -> (nn::account::NintendoAccountId, nn::account::nas::NasUserBaseForApplication, buffer) public ResultCode GetNintendoAccountUserResourceCacheForApplication(ServiceCtx context) { Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { NetworkServiceAccountId }); @@ -127,8 +112,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } - [Command(160)] // 5.0.0+ - // StoreOpenContext() public ResultCode StoreOpenContext(ServiceCtx context) { Logger.Stub?.PrintStub(LogClass.ServiceAcc); diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs new file mode 100644 index 0000000000..c6e0508f34 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AccountService/ProfileServer.cs @@ -0,0 +1,117 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.Utilities; +using System.IO; +using System.Reflection; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService +{ + class ProfileServer + { + private UserProfile _profile; + private Stream _profilePictureStream; + + public ProfileServer(UserProfile profile) + { + _profile = profile; + _profilePictureStream = Assembly.GetCallingAssembly().GetManifestResourceStream("Ryujinx.HLE.RyujinxProfileImage.jpg"); + } + + public ResultCode Get(ServiceCtx context) + { + context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80L); + + long bufferPosition = context.Request.RecvListBuff[0].Position; + + MemoryHelper.FillWithZeros(context.Memory, bufferPosition, 0x80); + + // TODO: Determine the struct. + context.Memory.Write((ulong)bufferPosition, 0); // Unknown + context.Memory.Write((ulong)bufferPosition + 4, 1); // Icon ID. 0 = Mii, the rest are character icon IDs. + context.Memory.Write((ulong)bufferPosition + 8, (byte)1); // Profile icon background color ID + // 0x07 bytes - Unknown + // 0x10 bytes - Some ID related to the Mii? All zeros when a character icon is used. + // 0x60 bytes - Usually zeros? + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return GetBase(context); + } + + 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; + } + + public ResultCode GetImageSize(ServiceCtx context) + { + context.ResponseData.Write(_profilePictureStream.Length); + + return ResultCode.Success; + } + + 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.Write((ulong)bufferPosition, profilePictureData); + + context.ResponseData.Write(_profilePictureStream.Length); + + return ResultCode.Success; + } + + public ResultCode Store(ServiceCtx context) + { + long userDataPosition = context.Request.PtrBuff[0].Position; + long userDataSize = context.Request.PtrBuff[0].Size; + + byte[] userData = new byte[userDataSize]; + + context.Memory.Read((ulong)userDataPosition, userData); + + // TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata. + + Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { userDataSize }); + + return ResultCode.Success; + } + + public ResultCode StoreWithImage(ServiceCtx context) + { + long userDataPosition = context.Request.PtrBuff[0].Position; + long userDataSize = context.Request.PtrBuff[0].Size; + + byte[] userData = new byte[userDataSize]; + + context.Memory.Read((ulong)userDataPosition, userData); + + long profilePicturePosition = context.Request.SendBuff[0].Position; + long profilePictureSize = context.Request.SendBuff[0].Size; + + byte[] profilePictureData = new byte[profilePictureSize]; + + context.Memory.Read((ulong)profilePicturePosition, profilePictureData); + + // TODO: Read the nn::account::profile::ProfileBase and store everything in the savedata. + + Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { userDataSize, profilePictureSize }); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs new file mode 100644 index 0000000000..4931866c42 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/ApplicationServiceServer.cs @@ -0,0 +1,223 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class ApplicationServiceServer + { + readonly AccountServiceFlag _serviceFlag; + + public ApplicationServiceServer(AccountServiceFlag serviceFlag) + { + _serviceFlag = serviceFlag; + } + + public ResultCode GetUserCountImpl(ServiceCtx context) + { + context.ResponseData.Write(context.Device.System.State.Account.GetUserCount()); + + return ResultCode.Success; + } + + public ResultCode GetUserExistenceImpl(ServiceCtx context) + { + ResultCode resultCode = CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _)); + + return ResultCode.Success; + } + + public ResultCode ListAllUsers(ServiceCtx context) + { + return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); + } + + public ResultCode ListOpenUsers(ServiceCtx context) + { + return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers()); + } + + private ResultCode WriteUserList(ServiceCtx context, IEnumerable profiles) + { + if (context.Request.RecvListBuff.Count == 0) + { + return ResultCode.InvalidInputBuffer; + } + + long outputPosition = context.Request.RecvListBuff[0].Position; + long outputSize = context.Request.RecvListBuff[0].Size; + + MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); + + ulong offset = 0; + + foreach (UserProfile userProfile in profiles) + { + if (offset + 0x10 > (ulong)outputSize) + { + break; + } + + context.Memory.Write((ulong)outputPosition + offset, userProfile.UserId.High); + context.Memory.Write((ulong)outputPosition + offset + 8, userProfile.UserId.Low); + + offset += 0x10; + } + + return ResultCode.Success; + } + + public ResultCode GetLastOpenedUser(ServiceCtx context) + { + context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData); + + return ResultCode.Success; + } + + public ResultCode GetProfile(ServiceCtx context, out IProfile profile) + { + profile = default; + + ResultCode resultCode = CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile)) + { + Logger.Warning?.Print(LogClass.ServiceAcc, $"User 0x{userId} not found!"); + + return ResultCode.UserNotFound; + } + + profile = new IProfile(userProfile); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) + { + context.ResponseData.Write(_serviceFlag != AccountServiceFlag.Application); + + return ResultCode.Success; + } + + public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) + { + if (context.Device.System.State.Account.GetUserCount() != 1) + { + // Invalid UserId. + UserId.Null.Write(context.ResponseData); + + return ResultCode.UserNotFound; + } + + bool isNetworkServiceAccountRequired = context.RequestData.ReadBoolean(); + + if (isNetworkServiceAccountRequired) + { + // NOTE: 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.Stub?.PrintStub(LogClass.ServiceAcc, new { isNetworkServiceAccountRequired }); + } + + // NOTE: 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; + } + + public ResultCode StoreSaveDataThumbnail(ServiceCtx context) + { + ResultCode resultCode = CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + 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 = new byte[inputSize]; + + context.Memory.Read((ulong)inputPosition, thumbnailBuffer); + + // NOTE: Account service call nn::fs::WriteSaveDataThumbnailFile(). + // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ? + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + public ResultCode ClearSaveDataThumbnail(ServiceCtx context) + { + ResultCode resultCode = CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + /* + // NOTE: Doesn't occur in our case. + if (userId == null) + { + return ResultCode.InvalidArgument; + } + */ + + // NOTE: Account service call nn::fs::WriteSaveDataThumbnailFileHeader(); + // TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ? + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + return ResultCode.Success; + } + + public ResultCode ListQualifiedUsers(ServiceCtx context) + { + // TODO: Determine how users are "qualified". We assume all users are "qualified" for now. + + return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); + } + + public ResultCode CheckUserId(ServiceCtx context, out UserId userId) + { + userId = context.RequestData.ReadStruct(); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + return ResultCode.Success; + } + } +} \ 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 index 9fb3fb9ba4..90091b08a2 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForAdministrator.cs @@ -1,8 +1,129 @@ -namespace Ryujinx.HLE.HOS.Services.Account.Acc +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc { - [Service("acc:su")] + [Service("acc:su", AccountServiceFlag.Administrator)] // Max Sessions: 8 class IAccountServiceForAdministrator : IpcService { - public IAccountServiceForAdministrator(ServiceCtx context) { } + private ApplicationServiceServer _applicationServiceServer; + + public IAccountServiceForAdministrator(ServiceCtx context, AccountServiceFlag serviceFlag) + { + _applicationServiceServer = new ApplicationServiceServer(serviceFlag); + } + + [Command(0)] + // GetUserCount() -> i32 + public ResultCode GetUserCount(ServiceCtx context) + { + return _applicationServiceServer.GetUserCountImpl(context); + } + + [Command(1)] + // GetUserExistence(nn::account::Uid) -> bool + public ResultCode GetUserExistence(ServiceCtx context) + { + return _applicationServiceServer.GetUserExistenceImpl(context); + } + + [Command(2)] + // ListAllUsers() -> array + public ResultCode ListAllUsers(ServiceCtx context) + { + return _applicationServiceServer.ListAllUsers(context); + } + + [Command(3)] + // ListOpenUsers() -> array + public ResultCode ListOpenUsers(ServiceCtx context) + { + return _applicationServiceServer.ListOpenUsers(context); + } + + [Command(4)] + // GetLastOpenedUser() -> nn::account::Uid + public ResultCode GetLastOpenedUser(ServiceCtx context) + { + return _applicationServiceServer.GetLastOpenedUser(context); + } + + [Command(5)] + // GetProfile(nn::account::Uid) -> object + public ResultCode GetProfile(ServiceCtx context) + { + ResultCode resultCode = _applicationServiceServer.GetProfile(context, out IProfile iProfile); + + if (resultCode == ResultCode.Success) + { + MakeObject(context, iProfile); + } + + return resultCode; + } + + [Command(50)] + // IsUserRegistrationRequestPermitted(pid) -> bool + public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) + { + // NOTE: pid is unused. + + return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); + } + + [Command(51)] + // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid + public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) + { + return _applicationServiceServer.TrySelectUserWithoutInteraction(context); + } + + [Command(102)] + // GetBaasAccountManagerForSystemService(nn::account::Uid) -> object + public ResultCode GetBaasAccountManagerForSystemService(ServiceCtx context) + { + ResultCode resultCode = _applicationServiceServer.CheckUserId(context, out UserId userId); + + if (resultCode != ResultCode.Success) + { + return resultCode; + } + + MakeObject(context, new IManagerForSystemService(userId)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + [Command(140)] // 6.0.0+ + // ListQualifiedUsers() -> array + public ResultCode ListQualifiedUsers(ServiceCtx context) + { + return _applicationServiceServer.ListQualifiedUsers(context); + } + + [Command(205)] + // GetProfileEditor(nn::account::Uid) -> object + public ResultCode GetProfileEditor(ServiceCtx context) + { + UserId userId = context.RequestData.ReadStruct(); + + if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile)) + { + Logger.Warning?.Print(LogClass.ServiceAcc, $"User 0x{userId} not found!"); + + return ResultCode.UserNotFound; + } + + MakeObject(context, new IProfileEditor(userProfile)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } } } \ 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 index 6382a4ba25..d238559413 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -1,188 +1,106 @@ -using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Cpu; +using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService; using Ryujinx.HLE.HOS.Services.Arp; -using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Account.Acc { - [Service("acc:u0")] + [Service("acc:u0", AccountServiceFlag.Application)] // Max Sessions: 4 class IAccountServiceForApplication : IpcService { - private bool _userRegistrationRequestPermitted = false; + private ApplicationServiceServer _applicationServiceServer; - private ApplicationLaunchProperty _applicationLaunchProperty; - - public IAccountServiceForApplication(ServiceCtx context) { } + public IAccountServiceForApplication(ServiceCtx context, AccountServiceFlag serviceFlag) + { + _applicationServiceServer = new ApplicationServiceServer(serviceFlag); + } [Command(0)] // GetUserCount() -> i32 public ResultCode GetUserCount(ServiceCtx context) { - context.ResponseData.Write(context.Device.System.State.Account.GetUserCount()); - - return ResultCode.Success; + return _applicationServiceServer.GetUserCountImpl(context); } [Command(1)] // GetUserExistence(nn::account::Uid) -> bool public ResultCode GetUserExistence(ServiceCtx context) { - UserId userId = context.RequestData.ReadStruct(); - - if (userId.IsNull) - { - return ResultCode.NullArgument; - } - - context.ResponseData.Write(context.Device.System.State.Account.TryGetUser(userId, out _)); - - return ResultCode.Success; + return _applicationServiceServer.GetUserExistenceImpl(context); } [Command(2)] // ListAllUsers() -> array public ResultCode ListAllUsers(ServiceCtx context) { - return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); + return _applicationServiceServer.ListAllUsers(context); } [Command(3)] // ListOpenUsers() -> array public ResultCode ListOpenUsers(ServiceCtx context) { - return WriteUserList(context, context.Device.System.State.Account.GetOpenedUsers()); - } - - private ResultCode WriteUserList(ServiceCtx context, IEnumerable profiles) - { - if (context.Request.RecvListBuff.Count == 0) - { - return ResultCode.InvalidInputBuffer; - } - - long outputPosition = context.Request.RecvListBuff[0].Position; - long outputSize = context.Request.RecvListBuff[0].Size; - - MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize); - - ulong offset = 0; - - foreach (UserProfile userProfile in profiles) - { - if (offset + 0x10 > (ulong)outputSize) - { - break; - } - - context.Memory.Write((ulong)outputPosition + offset, userProfile.UserId.High); - context.Memory.Write((ulong)outputPosition + offset + 8, userProfile.UserId.Low); - - offset += 0x10; - } - - return ResultCode.Success; + return _applicationServiceServer.ListOpenUsers(context); } [Command(4)] // GetLastOpenedUser() -> nn::account::Uid public ResultCode GetLastOpenedUser(ServiceCtx context) { - context.Device.System.State.Account.LastOpenedUser.UserId.Write(context.ResponseData); - - return ResultCode.Success; + return _applicationServiceServer.GetLastOpenedUser(context); } [Command(5)] // GetProfile(nn::account::Uid) -> object public ResultCode GetProfile(ServiceCtx context) { - UserId userId = context.RequestData.ReadStruct(); + ResultCode resultCode = _applicationServiceServer.GetProfile(context, out IProfile iProfile); - if (!context.Device.System.State.Account.TryGetUser(userId, out UserProfile userProfile)) + if (resultCode == ResultCode.Success) { - Logger.Warning?.Print(LogClass.ServiceAcc, $"User 0x{userId} not found!"); - - return ResultCode.UserNotFound; + MakeObject(context, iProfile); } - MakeObject(context, new IProfile(userProfile)); - - // Doesn't occur in our case. - // return ResultCode.NullObject; - - return ResultCode.Success; + return resultCode; } [Command(50)] - // IsUserRegistrationRequestPermitted(u64, pid) -> bool + // IsUserRegistrationRequestPermitted(pid) -> bool public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) { - // The u64 argument seems to be unused by account. - context.ResponseData.Write(_userRegistrationRequestPermitted); - - return ResultCode.Success; + // NOTE: pid is unused. + return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); } [Command(51)] // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) { - if (context.Device.System.State.Account.GetUserCount() != 1) - { - // Invalid UserId. - new UserId(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.Stub?.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; + return _applicationServiceServer.TrySelectUserWithoutInteraction(context); } [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? + // InitializeApplicationInfo(u64 pid_placeholder, pid) public ResultCode InitializeApplicationInfo(ServiceCtx context) { - if (_applicationLaunchProperty != null) - { - return ResultCode.Unknown41; - } + // NOTE: In call 100, account service use the pid_placeholder instead of the real pid, which is wrong, call 140 fix that. - // 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 + if (nn::arp::detail::IReader::GetApplicationLaunchProperty() == 0xCC9D) // ResultCode.InvalidProcessId { - _applicationLaunchProperty = ApplicationLaunchProperty.Default; - return ResultCode.InvalidArgument; } - else - */ - { - _applicationLaunchProperty = ApplicationLaunchProperty.GetByPid(context); - } - Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { unknown }); + */ + + // TODO: Determine where ApplicationLaunchProperty is used. + ApplicationLaunchProperty applicationLaunchProperty = ApplicationLaunchProperty.GetByPid(context); + + Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { applicationLaunchProperty.TitleId }); return ResultCode.Success; } @@ -191,19 +109,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // GetBaasAccountManagerForApplication(nn::account::Uid) -> object public ResultCode GetBaasAccountManagerForApplication(ServiceCtx context) { - UserId userId = context.RequestData.ReadStruct(); + ResultCode resultCode = _applicationServiceServer.CheckUserId(context, out UserId userId); - if (userId.IsNull) + if (resultCode != ResultCode.Success) { - return ResultCode.NullArgument; + return resultCode; } - if (_applicationLaunchProperty == null) - { - return ResultCode.InvalidArgument; - } - - MakeObject(context, new IManagerForApplication(userId, _applicationLaunchProperty)); + MakeObject(context, new IManagerForApplication(userId)); // Doesn't occur in our case. // return ResultCode.NullObject; @@ -215,63 +128,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // StoreSaveDataThumbnail(nn::account::Uid, buffer) public ResultCode StoreSaveDataThumbnail(ServiceCtx context) { - if (_applicationLaunchProperty == null) - { - return ResultCode.InvalidArgument; - } - - UserId userId = context.RequestData.ReadStruct(); - - 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 = new byte[inputSize]; - - context.Memory.Read((ulong)inputPosition, thumbnailBuffer); - - // TODO: Store thumbnailBuffer somewhere, in save data 0x8000000000000010 ? - - Logger.Stub?.PrintStub(LogClass.ServiceAcc); - - return ResultCode.Success; + return _applicationServiceServer.StoreSaveDataThumbnail(context); } [Command(111)] // ClearSaveDataThumbnail(nn::account::Uid) public ResultCode ClearSaveDataThumbnail(ServiceCtx context) { - if (_applicationLaunchProperty == null) - { - return ResultCode.InvalidArgument; - } - - UserId userId = context.RequestData.ReadStruct(); - - if (userId.IsNull) - { - return ResultCode.NullArgument; - } - - // TODO: Clear the Thumbnail somewhere, in save data 0x8000000000000010 ? - - Logger.Stub?.PrintStub(LogClass.ServiceAcc); - - return ResultCode.Success; + return _applicationServiceServer.ClearSaveDataThumbnail(context); } [Command(131)] // 6.0.0+ @@ -294,25 +158,15 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // ListQualifiedUsers() -> array public ResultCode ListQualifiedUsers(ServiceCtx context) { - // TODO: Determine how users are "qualified". We assume all users are "qualified" for now. - - return WriteUserList(context, context.Device.System.State.Account.GetAllUsers()); + return _applicationServiceServer.ListQualifiedUsers(context); } [Command(150)] // 6.0.0+ // IsUserAccountSwitchLocked() -> bool public ResultCode IsUserAccountSwitchLocked(ServiceCtx context) { - // TODO : Validate the following check. - /* - if (_applicationLaunchProperty != null) - { - return ResultCode.Unknown41; - } - */ - - // 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: 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.Application.ControlData.Value.UserAccountSwitchLock); @@ -321,4 +175,4 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs index f1972f6386..d61e0744b7 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForSystemService.cs @@ -1,8 +1,107 @@ -namespace Ryujinx.HLE.HOS.Services.Account.Acc +using Ryujinx.Common; +using Ryujinx.HLE.HOS.Services.Account.Acc.AccountService; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc { - [Service("acc:u1")] + [Service("acc:u1", AccountServiceFlag.SystemService)] // Max Sessions: 16 class IAccountServiceForSystemService : IpcService { - public IAccountServiceForSystemService(ServiceCtx context) { } + private ApplicationServiceServer _applicationServiceServer; + + public IAccountServiceForSystemService(ServiceCtx context, AccountServiceFlag serviceFlag) + { + _applicationServiceServer = new ApplicationServiceServer(serviceFlag); + } + + [Command(0)] + // GetUserCount() -> i32 + public ResultCode GetUserCount(ServiceCtx context) + { + return _applicationServiceServer.GetUserCountImpl(context); + } + + [Command(1)] + // GetUserExistence(nn::account::Uid) -> bool + public ResultCode GetUserExistence(ServiceCtx context) + { + return _applicationServiceServer.GetUserExistenceImpl(context); + } + + [Command(2)] + // ListAllUsers() -> array + public ResultCode ListAllUsers(ServiceCtx context) + { + return _applicationServiceServer.ListAllUsers(context); + } + + [Command(3)] + // ListOpenUsers() -> array + public ResultCode ListOpenUsers(ServiceCtx context) + { + return _applicationServiceServer.ListOpenUsers(context); + } + + [Command(4)] + // GetLastOpenedUser() -> nn::account::Uid + public ResultCode GetLastOpenedUser(ServiceCtx context) + { + return _applicationServiceServer.GetLastOpenedUser(context); + } + + [Command(5)] + // GetProfile(nn::account::Uid) -> object + public ResultCode GetProfile(ServiceCtx context) + { + ResultCode resultCode = _applicationServiceServer.GetProfile(context, out IProfile iProfile); + + if (resultCode == ResultCode.Success) + { + MakeObject(context, iProfile); + } + + return resultCode; + } + + [Command(50)] + // IsUserRegistrationRequestPermitted(pid) -> bool + public ResultCode IsUserRegistrationRequestPermitted(ServiceCtx context) + { + // NOTE: pid is unused. + + return _applicationServiceServer.IsUserRegistrationRequestPermitted(context); + } + + [Command(51)] + // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid + public ResultCode TrySelectUserWithoutInteraction(ServiceCtx context) + { + return _applicationServiceServer.TrySelectUserWithoutInteraction(context); + } + + [Command(102)] + // GetBaasAccountManagerForSystemService(nn::account::Uid) -> object + public ResultCode GetBaasAccountManagerForSystemService(ServiceCtx context) + { + UserId userId = context.RequestData.ReadStruct(); + + if (userId.IsNull) + { + return ResultCode.NullArgument; + } + + MakeObject(context, new IManagerForSystemService(userId)); + + // Doesn't occur in our case. + // return ResultCode.NullObject; + + return ResultCode.Success; + } + + [Command(140)] // 6.0.0+ + // ListQualifiedUsers() -> array + public ResultCode ListQualifiedUsers(ServiceCtx context) + { + return _applicationServiceServer.ListQualifiedUsers(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 index d28ea2753f..223be2f5e0 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IBaasAccessTokenAccessor.cs @@ -1,6 +1,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { - [Service("acc:aa")] + [Service("acc:aa", AccountServiceFlag.BaasAccessTokenAccessor)] // Max Sessions: 4 class IBaasAccessTokenAccessor : IpcService { public IBaasAccessTokenAccessor(ServiceCtx context) { } diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs deleted file mode 100644 index 4b134c6f45..0000000000 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IProfile.cs +++ /dev/null @@ -1,82 +0,0 @@ -using Ryujinx.Common.Logging; -using Ryujinx.Cpu; -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) - public ResultCode Get(ServiceCtx context) - { - Logger.Stub?.PrintStub(LogClass.ServiceAcc); - - context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(0x80L); - - long position = context.Request.ReceiveBuff[0].Position; - - MemoryHelper.FillWithZeros(context.Memory, position, 0x80); - - context.Memory.Write((ulong)position, 0); - context.Memory.Write((ulong)position + 4, 1); - context.Memory.Write((ulong)position + 8, 1L); - - 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) - 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.Write((ulong)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/AccountServiceFlag.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs new file mode 100644 index 0000000000..a991f9776e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/AccountServiceFlag.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + enum AccountServiceFlag + { + Administrator = 100, + SystemService = 101, + Application = 102, + BaasAccessTokenAccessor = 200 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs index 75915c02fd..85ddb4393b 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/Types/UserId.cs @@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc public bool IsNull => (Low | High) == 0; + public static UserId Null => new UserId(0, 0); + public UserId(long low, long high) { Low = low; diff --git a/Ryujinx.HLE/HOS/Services/Arp/IReader.cs b/Ryujinx.HLE/HOS/Services/Arp/IReader.cs index 5d1e2fff23..35a2de0c13 100644 --- a/Ryujinx.HLE/HOS/Services/Arp/IReader.cs +++ b/Ryujinx.HLE/HOS/Services/Arp/IReader.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.HOS.Services.Hid +namespace Ryujinx.HLE.HOS.Services.Arp { [Service("arp:r")] class IReader : IpcService diff --git a/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs b/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs index 44d674a494..dc8ed2e6c4 100644 --- a/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs +++ b/Ryujinx.HLE/HOS/Services/Arp/LibHacIReader.cs @@ -1,5 +1,4 @@ using LibHac; -using LibHac.Arp.Impl; using LibHac.Ncm; using LibHac.Ns; using System; @@ -8,7 +7,7 @@ using ApplicationId = LibHac.ApplicationId; namespace Ryujinx.HLE.HOS.Services.Arp { - class LibHacIReader : IReader + class LibHacIReader : LibHac.Arp.Impl.IReader { private Horizon System { get; } @@ -19,21 +18,22 @@ namespace Ryujinx.HLE.HOS.Services.Arp public Result GetApplicationLaunchProperty(out LibHac.Arp.ApplicationLaunchProperty launchProperty, ulong processId) { - launchProperty = new LibHac.Arp.ApplicationLaunchProperty(); - - launchProperty.BaseStorageId = StorageId.BuiltInUser; - launchProperty.ApplicationId = new ApplicationId(System.Device.Application.TitleId); + launchProperty = new LibHac.Arp.ApplicationLaunchProperty + { + BaseStorageId = StorageId.BuiltInUser, + ApplicationId = new ApplicationId(System.Device.Application.TitleId) + }; return Result.Success; } - public Result GetApplicationLaunchPropertyWithApplicationId(out LibHac.Arp.ApplicationLaunchProperty launchProperty, - ApplicationId applicationId) + public Result GetApplicationLaunchPropertyWithApplicationId(out LibHac.Arp.ApplicationLaunchProperty launchProperty, ApplicationId applicationId) { - launchProperty = new LibHac.Arp.ApplicationLaunchProperty(); - - launchProperty.BaseStorageId = StorageId.BuiltInUser; - launchProperty.ApplicationId = applicationId; + launchProperty = new LibHac.Arp.ApplicationLaunchProperty + { + BaseStorageId = StorageId.BuiltInUser, + ApplicationId = applicationId + }; return Result.Success; } @@ -43,10 +43,9 @@ namespace Ryujinx.HLE.HOS.Services.Arp throw new NotImplementedException(); } - public Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, - ApplicationId applicationId) + public Result GetApplicationControlPropertyWithApplicationId(out ApplicationControlProperty controlProperty, ApplicationId applicationId) { throw new NotImplementedException(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs b/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs index 4324f13e35..ea200f3d54 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IServiceCreator.cs @@ -1,7 +1,6 @@ using Ryujinx.Common; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Friend.ServiceCreator; -using Ryujinx.HLE.Utilities; namespace Ryujinx.HLE.HOS.Services.Friend {