From 3dcee8c437635802001099bd3a38eb23d83345a8 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Wed, 24 Nov 2021 22:11:50 +0100
Subject: [PATCH] account/ns: Implement 13.0.0+ service calls (#2820)

This PR implements/stubs some missing calls introduced in recent firmware (13.0.0).

- `acc:u0 InitializeApplicationInfoV2` needed by ACNH.
- `aoc:u NotifyMountAddOnContent/NotifyUnmountAddOnContent/CheckAddOnContentMountStatus` checked by RE. Needed by ACNH.
- `IPurchaseEventManager PopPurchasedProductInfo` needed by Dying Light.

Now ACNH 2.0 update is fully playable, and Dying Light can boot further:
---
 .../Acc/IAccountServiceForApplication.cs      |  1 +
 .../Services/Ns/Aoc/IAddOnContentManager.cs   | 49 +++++++++++++++++++
 .../Services/Ns/Aoc/IPurchaseEventManager.cs  | 19 ++++++-
 3 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
index 2fbf950c9..c29ed570c 100644
--- a/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
+++ b/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs
@@ -81,6 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
 
         [CommandHipc(100)]
         [CommandHipc(140)] // 6.0.0+
+        [CommandHipc(160)] // 13.0.0+
         // InitializeApplicationInfo(u64 pid_placeholder, pid)
         public ResultCode InitializeApplicationInfo(ServiceCtx context)
         {
diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs
index 2f35a8b87..18e371da5 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs
@@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
 
         private ulong _addOnContentBaseId;
 
+        private List<ulong> _mountedAocTitleIds = new List<ulong>();
+
         public IAddOnContentManager(ServiceCtx context)
         {
             _addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext);
@@ -136,6 +138,53 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
             return GetAddOnContentListChangedEventImpl(context);
         }
 
+        [CommandHipc(11)] // 13.0.0+
+        // NotifyMountAddOnContent(pid, u64 title_id)
+        public ResultCode NotifyMountAddOnContent(ServiceCtx context)
+        {
+            long pid = context.Request.HandleDesc.PId;
+
+            // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+            ulong aocTitleId = context.RequestData.ReadUInt64();
+
+            if (_mountedAocTitleIds.Count <= 0x7F)
+            {
+                _mountedAocTitleIds.Add(aocTitleId);
+            }
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(12)] // 13.0.0+
+        // NotifyUnmountAddOnContent(pid, u64 title_id)
+        public ResultCode NotifyUnmountAddOnContent(ServiceCtx context)
+        {
+            long pid = context.Request.HandleDesc.PId;
+
+            // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+            ulong aocTitleId = context.RequestData.ReadUInt64();
+
+            _mountedAocTitleIds.Remove(aocTitleId);
+
+            return ResultCode.Success;
+        }
+
+        [CommandHipc(50)] // 13.0.0+
+        // CheckAddOnContentMountStatus(pid)
+        public ResultCode CheckAddOnContentMountStatus(ServiceCtx context)
+        {
+            long pid = context.Request.HandleDesc.PId;
+
+            // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+            //       Then it does some internal checks and returns InvalidBufferSize if they fail.
+
+            Logger.Stub?.PrintStub(LogClass.ServiceNs);
+
+            return ResultCode.Success;
+        }
+
         [CommandHipc(100)] // 7.0.0+
         // CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
         public ResultCode CreateEcPurchasedEventManager(ServiceCtx context)
diff --git a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs
index eb2b03208..9b65e0f9e 100644
--- a/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs
@@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
 
             context.Memory.Read(inBufferPosition, buffer);
 
-            // NOTE: Service use the pid to call arp:r GetApplicationLaunchProperty and store it in internal field.
+            // NOTE: Service uses the pid to call arp:r GetApplicationLaunchProperty and store it in internal field.
             //       Then it seems to use the buffer content and compare it with a stored linked instrusive list.
             //       Since we don't support purchase from eShop, we can stub it.
 
@@ -47,5 +47,22 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
 
             return ResultCode.Success;
         }
+
+        [CommandHipc(3)]
+        // PopPurchasedProductInfo(nn::ec::detail::PurchasedProductInfo)
+        public ResultCode PopPurchasedProductInfo(ServiceCtx context)
+        {
+            byte[] purchasedProductInfo = new byte[0x80];
+
+            context.ResponseData.Write(purchasedProductInfo);
+
+            // NOTE: Service finds info using internal array then convert it into nn::ec::detail::PurchasedProductInfo.
+            //       Returns 0x320A4 if the internal array size is null.
+            //       Since we don't support purchase from eShop, we can stub it.
+
+            Logger.Debug?.PrintStub(LogClass.ServiceNs); // NOTE: Uses Debug to avoid spamming.
+
+            return ResultCode.Success;
+        }
     }
 }
\ No newline at end of file