From 5eb0ee3ccaf531ad4042199f848e9462efb3d8ec Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Tue, 18 Aug 2020 21:24:54 +0200
Subject: [PATCH] account: Implement IManagerForApplication calls and
 IAsyncContext (#1466)

* account: Implement IManagerForApplication calls and IAsyncContext

This implement:
- IManagerForApplication::EnsureIdTokenCacheAsync (accordingly to RE) but the Async task is stubbed.
- IAsyncContext interface (accordingly to RE).
- IManagerForApplication::LoadIdTokenCache (checked with RE, and stubbed).

I've tried some games but now they needs some `sfdnsres` calls, some other boots and crashes with other issues.
Maybe we should disable the connection somewhere to lets the game think we are offline. I have done many attempts, without success, but since the code is here now, it's better than nothing.

(I've cleaned up `using` of IGeneralService too)

Closes #629 and closes #630

* change AccountId

* Fix gdkchan's comments

* use CompletedTask
---
 .../Acc/AsyncContext/AsyncExecution.cs        | 56 +++++++++++++
 .../Acc/IAccountServiceForApplication.cs      |  4 +-
 .../HOS/Services/Account/Acc/IAsyncContext.cs | 79 ++++++++++++++++++
 .../Account/Acc/IManagerForApplication.cs     | 82 ++++++++++++++++++-
 .../HOS/Services/Account/ResultCode.cs        |  6 +-
 .../Nifm/StaticService/IGeneralService.cs     |  5 --
 6 files changed, 221 insertions(+), 11 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/AsyncContext/AsyncExecution.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Account/Acc/IAsyncContext.cs

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 0000000000..2ea92b1171
--- /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<CancellationToken, Task> 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 9678a4ca4b..d1bf94e755 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 0000000000..1bbe24f130
--- /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<copy>
+        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 70c5965ef0..54565cb1a1 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<nn::account::detail::IAsyncContext>
+        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<bytes, 6>)
+        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<nn::account::nas::NasUserBaseForApplication, 0x1a>, buffer<bytes, 6>)
         public ResultCode GetNintendoAccountUserResourceCacheForApplication(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Account/ResultCode.cs
index cf61ed5c6b..f25b2acb2a 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 d3b5ce3e7f..1b170f4b43 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
 {