diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs new file mode 100644 index 000000000..2ea92b117 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs @@ -0,0 +1,56 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext +{ + class AsyncExecution + { + private readonly CancellationTokenSource _tokenSource; + private readonly CancellationToken _token; + + public KEvent SystemEvent { get; } + public bool IsInitialized { get; private set; } + public bool IsRunning { get; private set; } + + public AsyncExecution(KEvent asyncEvent) + { + SystemEvent = asyncEvent; + + _tokenSource = new CancellationTokenSource(); + _token = _tokenSource.Token; + } + + public void Initialize(int timeout, Func taskAsync) + { + Task.Run(async () => + { + IsRunning = true; + + _tokenSource.CancelAfter(timeout); + + try + { + await taskAsync(_token); + } + catch (Exception ex) + { + Logger.Warning?.Print(LogClass.ServiceAcc, $"Exception: {ex.Message}"); + } + + SystemEvent.ReadableEvent.Signal(); + + IsRunning = false; + }, _token); + + IsInitialized = true; + } + + public void Cancel() + { + _tokenSource.Cancel(); + } + } +} \ 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 9678a4ca4..d1bf94e75 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { if (_applicationLaunchProperty != null) { - return ResultCode.ApplicationLaunchPropertyAlreadyInit; + return ResultCode.Unknown41; } // The u64 argument seems to be unused by account. @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc /* if (_applicationLaunchProperty != null) { - return ResultCode.ApplicationLaunchPropertyAlreadyInit; + return ResultCode.Unknown41; } */ diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs new file mode 100644 index 000000000..1bbe24f13 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs @@ -0,0 +1,79 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext; +using System; + +namespace Ryujinx.HLE.HOS.Services.Account.Acc +{ + class IAsyncContext : IpcService + { + AsyncExecution _asyncExecution; + + public IAsyncContext(AsyncExecution asyncExecution) + { + _asyncExecution = asyncExecution; + } + + [Command(0)] + // GetSystemEvent() -> handle + public ResultCode GetSystemEvent(ServiceCtx context) + { + if (context.Process.HandleTable.GenerateHandle(_asyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_systemEventHandle); + + return ResultCode.Success; + } + + [Command(1)] + // Cancel() + public ResultCode Cancel(ServiceCtx context) + { + if (!_asyncExecution.IsInitialized) + { + return ResultCode.AsyncExecutionNotInitialized; + } + + if (_asyncExecution.IsRunning) + { + _asyncExecution.Cancel(); + } + + return ResultCode.Success; + } + + [Command(2)] + // HasDone() -> b8 + public ResultCode HasDone(ServiceCtx context) + { + if (!_asyncExecution.IsInitialized) + { + return ResultCode.AsyncExecutionNotInitialized; + } + + context.ResponseData.Write(_asyncExecution.SystemEvent.ReadableEvent.IsSignaled()); + + return ResultCode.Success; + } + + [Command(3)] + // GetResult() + public ResultCode GetResult(ServiceCtx context) + { + if (!_asyncExecution.IsInitialized) + { + return ResultCode.AsyncExecutionNotInitialized; + } + + if (!_asyncExecution.SystemEvent.ReadableEvent.IsSignaled()) + { + return ResultCode.Unknown41; + } + + return ResultCode.Success; + } + } +} \ 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 index 70c5965ef..54565cb1a 100644 --- a/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IManagerForApplication.cs @@ -1,15 +1,23 @@ 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 { class IManagerForApplication : IpcService { + // TODO: Determine where and how NetworkServiceAccountId is set. + private const long NetworkServiceAccountId = 0xcafe; + private UserId _userId; private ApplicationLaunchProperty _applicationLaunchProperty; - private const long NetworkServiceAccountId = 0xcafe; - public IManagerForApplication(UserId userId, ApplicationLaunchProperty applicationLaunchProperty) { _userId = userId; @@ -20,8 +28,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // 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". + // Then it searches the Availability of Online Services related to the UserId in this file and returns it. + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + // NOTE: Even if we try to return different error codes here, the guest still needs other calls. return ResultCode.Success; } @@ -29,6 +41,10 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // 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 + // as "%08x-%04x-%04x-%02x%02x-%08x%04x") in the account:/ savedata. + // Then it searches the NetworkServiceAccountId related to the UserId in this file and returns it. + Logger.Stub?.PrintStub(LogClass.ServiceAcc, new { NetworkServiceAccountId }); context.ResponseData.Write(NetworkServiceAccountId); @@ -36,6 +52,68 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc return ResultCode.Success; } + [Command(2)] + // EnsureIdTokenCacheAsync() -> object + public ResultCode EnsureIdTokenCacheAsync(ServiceCtx context) + { + KEvent asyncEvent = new KEvent(context.Device.System.KernelContext); + AsyncExecution asyncExecution = new AsyncExecution(asyncEvent); + + asyncExecution.Initialize(1000, EnsureIdTokenCacheAsyncImpl); + + MakeObject(context, new IAsyncContext(asyncExecution)); + + // return ResultCode.NullObject if the IAsyncContext pointer is null. Doesn't occur in our case. + + return ResultCode.Success; + } + + private async Task EnsureIdTokenCacheAsyncImpl(CancellationToken token) + { + // NOTE: This open 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") + // in the "account:/" savedata. + // Then its read data, use dauth API with this data to get the Token Id and probably store the dauth response + // in "su/cache/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x") in the "account:/" savedata. + // Since we don't support online services, we can stub it. + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + // TODO: Use a real function instead, with the CancellationToken. + await Task.CompletedTask; + } + + [Command(3)] + // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer) + public ResultCode LoadIdTokenCache(ServiceCtx context) + { + long bufferPosition = context.Request.ReceiveBuff[0].Position; + long bufferSize = context.Request.ReceiveBuff[0].Size; + + // NOTE: This opens the file at "su/cache/USERID_IN_UUID_STRING.dat" (where USERID_IN_UUID_STRING is formatted as "%08x-%04x-%04x-%02x%02x-%08x%04x") + // in the "account:/" savedata and writes some data in the buffer. + // Since we don't support online services, we can stub it. + + Logger.Stub?.PrintStub(LogClass.ServiceAcc); + + /* + if (internal_object != null) + { + if (bufferSize > 0xC00) + { + return ResultCode.InvalidIdTokenCacheBufferSize; + } + } + */ + + int idTokenCacheSize = 0; + + MemoryHelper.FillWithZeros(context.Memory, bufferPosition, (int)bufferSize); + + context.ResponseData.Write(idTokenCacheSize); + + return ResultCode.Success; + } + [Command(130)] // GetNintendoAccountUserResourceCacheForApplication() -> (nn::account::NintendoAccountId, buffer, buffer) public ResultCode GetNintendoAccountUserResourceCacheForApplication(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs index cf61ed5c6..f25b2acb2 100644 --- a/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs @@ -12,10 +12,12 @@ namespace Ryujinx.HLE.HOS.Services.Account NullInputBuffer = (30 << ErrorCodeShift) | ModuleId, InvalidInputBufferSize = (31 << ErrorCodeShift) | ModuleId, InvalidInputBuffer = (32 << ErrorCodeShift) | ModuleId, - ApplicationLaunchPropertyAlreadyInit = (41 << ErrorCodeShift) | ModuleId, + AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId, + Unknown41 = (41 << ErrorCodeShift) | ModuleId, InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId, UserNotFound = (100 << ErrorCodeShift) | ModuleId, NullObject = (302 << ErrorCodeShift) | ModuleId, - UnknownError1 = (341 << ErrorCodeShift) | ModuleId + Unknown341 = (341 << ErrorCodeShift) | ModuleId, + InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId } } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index d3b5ce3e7..1b170f4b4 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -3,12 +3,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService; using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types; using System; -using System.Diagnostics; -using System.Linq; -using System.Net; using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Runtime.CompilerServices; namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService {