aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Services/Ns
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Ns')
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs346
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs7
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs68
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs13
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs28
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs26
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs30
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs8
-rw-r--r--src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs8
10 files changed, 542 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs
new file mode 100644
index 00000000..b4b5bb1f
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs
@@ -0,0 +1,346 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Horizon.Common;
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
+{
+ [Service("aoc:u")]
+ class IAddOnContentManager : IpcService
+ {
+ private readonly KEvent _addOnContentListChangedEvent;
+ private int _addOnContentListChangedEventHandle;
+
+ private ulong _addOnContentBaseId;
+
+ private List<ulong> _mountedAocTitleIds = new List<ulong>();
+
+ public IAddOnContentManager(ServiceCtx context)
+ {
+ _addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext);
+ }
+
+ [CommandCmif(0)] // 1.0.0-6.2.0
+ // CountAddOnContentByApplicationId(u64 title_id) -> u32
+ public ResultCode CountAddOnContentByApplicationId(ServiceCtx context)
+ {
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ return CountAddOnContentImpl(context, titleId);
+ }
+
+ [CommandCmif(1)] // 1.0.0-6.2.0
+ // ListAddOnContentByApplicationId(u64 title_id, u32 start_index, u32 buffer_size) -> (u32 count, buffer<u32>)
+ public ResultCode ListAddOnContentByApplicationId(ServiceCtx context)
+ {
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ return ListAddContentImpl(context, titleId);
+ }
+
+ [CommandCmif(2)]
+ // CountAddOnContent(pid) -> u32
+ public ResultCode CountAddOnContent(ServiceCtx context)
+ {
+ ulong pid = context.Request.HandleDesc.PId;
+
+ // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+ return CountAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId);
+ }
+
+ [CommandCmif(3)]
+ // ListAddOnContent(u32 start_index, u32 buffer_size, pid) -> (u32 count, buffer<u32>)
+ public ResultCode ListAddOnContent(ServiceCtx context)
+ {
+ ulong pid = context.Request.HandleDesc.PId;
+
+ // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+ return ListAddContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId);
+ }
+
+ [CommandCmif(4)] // 1.0.0-6.2.0
+ // GetAddOnContentBaseIdByApplicationId(u64 title_id) -> u64
+ public ResultCode GetAddOnContentBaseIdByApplicationId(ServiceCtx context)
+ {
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ return GetAddOnContentBaseIdImpl(context, titleId);
+ }
+
+ [CommandCmif(5)]
+ // GetAddOnContentBaseId(pid) -> u64
+ public ResultCode GetAddOnContentBaseId(ServiceCtx context)
+ {
+ ulong pid = context.Request.HandleDesc.PId;
+
+ // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+ return GetAddOnContentBaseIdImpl(context, context.Device.Processes.ActiveApplication.ProgramId);
+ }
+
+ [CommandCmif(6)] // 1.0.0-6.2.0
+ // PrepareAddOnContentByApplicationId(u64 title_id, u32 index)
+ public ResultCode PrepareAddOnContentByApplicationId(ServiceCtx context)
+ {
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ return PrepareAddOnContentImpl(context, titleId);
+ }
+
+ [CommandCmif(7)]
+ // PrepareAddOnContent(u32 index, pid)
+ public ResultCode PrepareAddOnContent(ServiceCtx context)
+ {
+ ulong pid = context.Request.HandleDesc.PId;
+
+ // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+ return PrepareAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId);
+ }
+
+ [CommandCmif(8)] // 4.0.0+
+ // GetAddOnContentListChangedEvent() -> handle<copy>
+ public ResultCode GetAddOnContentListChangedEvent(ServiceCtx context)
+ {
+ return GetAddOnContentListChangedEventImpl(context);
+ }
+
+ [CommandCmif(9)] // 10.0.0+
+ // GetAddOnContentLostErrorCode() -> u64
+ public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context)
+ {
+ // NOTE: 0x7D0A4 -> 2164-1000
+ context.ResponseData.Write(GetAddOnContentLostErrorCodeImpl(0x7D0A4));
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(10)] // 11.0.0+
+ // GetAddOnContentListChangedEventWithProcessId(pid) -> handle<copy>
+ public ResultCode GetAddOnContentListChangedEventWithProcessId(ServiceCtx context)
+ {
+ ulong pid = context.Request.HandleDesc.PId;
+
+ // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
+
+ // TODO: Found where stored value is used.
+ ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId);
+
+ if (resultCode != ResultCode.Success)
+ {
+ return resultCode;
+ }
+
+ return GetAddOnContentListChangedEventImpl(context);
+ }
+
+ [CommandCmif(11)] // 13.0.0+
+ // NotifyMountAddOnContent(pid, u64 title_id)
+ public ResultCode NotifyMountAddOnContent(ServiceCtx context)
+ {
+ ulong 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;
+ }
+
+ [CommandCmif(12)] // 13.0.0+
+ // NotifyUnmountAddOnContent(pid, u64 title_id)
+ public ResultCode NotifyUnmountAddOnContent(ServiceCtx context)
+ {
+ ulong 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;
+ }
+
+ [CommandCmif(50)] // 13.0.0+
+ // CheckAddOnContentMountStatus(pid)
+ public ResultCode CheckAddOnContentMountStatus(ServiceCtx context)
+ {
+ ulong 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;
+ }
+
+ [CommandCmif(100)] // 7.0.0+
+ // CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
+ public ResultCode CreateEcPurchasedEventManager(ServiceCtx context)
+ {
+ MakeObject(context, new IPurchaseEventManager(context.Device.System));
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(101)] // 9.0.0+
+ // CreatePermanentEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
+ public ResultCode CreatePermanentEcPurchasedEventManager(ServiceCtx context)
+ {
+ // NOTE: Service call arp:r to get the TitleId, do some extra checks and pass it to returned interface.
+
+ MakeObject(context, new IPurchaseEventManager(context.Device.System));
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(110)] // 12.0.0+
+ // CreateContentsServiceManager() -> object<nn::ec::IContentsServiceManager>
+ public ResultCode CreateContentsServiceManager(ServiceCtx context)
+ {
+ MakeObject(context, new IContentsServiceManager());
+
+ return ResultCode.Success;
+ }
+
+ private ResultCode CountAddOnContentImpl(ServiceCtx context, ulong titleId)
+ {
+ // NOTE: Service call sys:set GetQuestFlag and store it internally.
+ // If QuestFlag is true, counts some extra titles.
+
+ ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, titleId);
+
+ if (resultCode != ResultCode.Success)
+ {
+ return resultCode;
+ }
+
+ // TODO: This should use _addOnContentBaseId;
+ uint aocCount = (uint)context.Device.System.ContentManager.GetAocCount();
+
+ context.ResponseData.Write(aocCount);
+
+ return ResultCode.Success;
+ }
+
+ private ResultCode ListAddContentImpl(ServiceCtx context, ulong titleId)
+ {
+ // NOTE: Service call sys:set GetQuestFlag and store it internally.
+ // If QuestFlag is true, counts some extra titles.
+
+ uint startIndex = context.RequestData.ReadUInt32();
+ uint indexNumber = context.RequestData.ReadUInt32();
+ ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
+ ulong bufferSize = context.Request.ReceiveBuff[0].Size;
+
+ // TODO: This should use _addOnContentBaseId;
+ uint aocTotalCount = (uint)context.Device.System.ContentManager.GetAocCount();
+
+ if (indexNumber > bufferSize / sizeof(uint))
+ {
+ return ResultCode.InvalidBufferSize;
+ }
+
+ if (aocTotalCount <= startIndex)
+ {
+ context.ResponseData.Write(0);
+
+ return ResultCode.Success;
+ }
+
+ IList<ulong> aocTitleIds = context.Device.System.ContentManager.GetAocTitleIds();
+
+ GetAddOnContentBaseIdFromTitleId(context, titleId);
+
+ uint indexCounter = 0;
+
+ for (int i = 0; i < indexNumber; i++)
+ {
+ if (i + (int)startIndex < aocTitleIds.Count)
+ {
+ context.Memory.Write(bufferPosition + (ulong)i * sizeof(uint), (uint)(aocTitleIds[i + (int)startIndex] - _addOnContentBaseId));
+
+ indexCounter++;
+ }
+ }
+
+ context.ResponseData.Write(indexCounter);
+
+ return ResultCode.Success;
+ }
+
+ private ResultCode GetAddOnContentBaseIdImpl(ServiceCtx context, ulong titleId)
+ {
+ ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, titleId);
+
+ context.ResponseData.Write(_addOnContentBaseId);
+
+ return resultCode;
+ }
+
+ private ResultCode GetAddOnContentBaseIdFromTitleId(ServiceCtx context, ulong titleId)
+ {
+ // NOTE: Service calls arp:r GetApplicationControlProperty to get AddOnContentBaseId using TitleId,
+ // If the call fails, it returns ResultCode.InvalidPid.
+
+ _addOnContentBaseId = context.Device.Processes.ActiveApplication.ApplicationControlProperties.AddOnContentBaseId;
+
+ if (_addOnContentBaseId == 0)
+ {
+ _addOnContentBaseId = titleId + 0x1000;
+ }
+
+ return ResultCode.Success;
+ }
+
+ private ResultCode PrepareAddOnContentImpl(ServiceCtx context, ulong titleId)
+ {
+ uint index = context.RequestData.ReadUInt32();
+
+ ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId);
+
+ if (resultCode != ResultCode.Success)
+ {
+ return resultCode;
+ }
+
+ // TODO: Service calls ns:am RegisterContentsExternalKey?, GetOwnedApplicationContentMetaStatus? etc...
+ // Ideally, this should probably initialize the AocData values for the specified index
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNs, new { index });
+
+ return ResultCode.Success;
+ }
+
+ private ResultCode GetAddOnContentListChangedEventImpl(ServiceCtx context)
+ {
+ if (_addOnContentListChangedEventHandle == 0)
+ {
+ if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != Result.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_addOnContentListChangedEventHandle);
+
+ return ResultCode.Success;
+ }
+
+ private static ulong GetAddOnContentLostErrorCodeImpl(int errorCode)
+ {
+ return ((ulong)errorCode & 0x1FF | ((((ulong)errorCode >> 9) & 0x1FFF) << 32)) + 2000;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs
new file mode 100644
index 00000000..cb8903d4
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IContentsServiceManager.cs
@@ -0,0 +1,7 @@
+namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
+{
+ class IContentsServiceManager : IpcService
+ {
+ public IContentsServiceManager() { }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs
new file mode 100644
index 00000000..1673fafc
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IPurchaseEventManager.cs
@@ -0,0 +1,68 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.Horizon.Common;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
+{
+ class IPurchaseEventManager : IpcService
+ {
+ private readonly KEvent _purchasedEvent;
+
+ public IPurchaseEventManager(Horizon system)
+ {
+ _purchasedEvent = new KEvent(system.KernelContext);
+ }
+
+ [CommandCmif(0)]
+ // SetDefaultDeliveryTarget(pid, buffer<bytes, 5> unknown)
+ public ResultCode SetDefaultDeliveryTarget(ServiceCtx context)
+ {
+ ulong inBufferPosition = context.Request.SendBuff[0].Position;
+ ulong inBufferSize = context.Request.SendBuff[0].Size;
+ byte[] buffer = new byte[inBufferSize];
+
+ context.Memory.Read(inBufferPosition, buffer);
+
+ // 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.
+
+ Logger.Stub?.PrintStub(LogClass.ServiceNs);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(2)]
+ // GetPurchasedEventReadableHandle() -> handle<copy, event>
+ public ResultCode GetPurchasedEventReadableHandle(ServiceCtx context)
+ {
+ if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int purchasedEventReadableHandle) != Result.Success)
+ {
+ throw new InvalidOperationException("Out of handles!");
+ }
+
+ context.Response.HandleDesc = IpcHandleDesc.MakeCopy(purchasedEventReadableHandle);
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(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
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs
new file mode 100644
index 00000000..7602ecb3
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/ResultCode.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
+{
+ enum ResultCode
+ {
+ ModuleId = 166,
+ ErrorCodeShift = 9,
+
+ Success = 0,
+
+ InvalidBufferSize = (200 << ErrorCodeShift) | ModuleId,
+ InvalidPid = (300 << ErrorCodeShift) | ModuleId
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
new file mode 100644
index 00000000..06e911f8
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs
@@ -0,0 +1,28 @@
+using LibHac.Ns;
+using Ryujinx.Common.Utilities;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ [Service("ns:am")]
+ class IApplicationManagerInterface : IpcService
+ {
+ public IApplicationManagerInterface(ServiceCtx context) { }
+
+ [CommandCmif(400)]
+ // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>)
+ public ResultCode GetApplicationControlData(ServiceCtx context)
+ {
+ byte source = (byte)context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ ulong position = context.Request.ReceiveBuff[0].Position;
+
+ ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
+
+ context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray());
+
+ return ResultCode.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs
new file mode 100644
index 00000000..c74ebd69
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/IDevelopInterface.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ [Service("ns:dev")]
+ class IDevelopInterface : IpcService
+ {
+ public IDevelopInterface(ServiceCtx context) { }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs
new file mode 100644
index 00000000..aa37a1e7
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs
@@ -0,0 +1,26 @@
+using LibHac.Common;
+using LibHac.Ns;
+
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ class IReadOnlyApplicationControlDataInterface : IpcService
+ {
+ public IReadOnlyApplicationControlDataInterface(ServiceCtx context) { }
+
+ [CommandCmif(0)]
+ // GetApplicationControlData(u8, u64) -> (unknown<4>, buffer<unknown, 6>)
+ public ResultCode GetApplicationControlData(ServiceCtx context)
+ {
+ byte source = (byte)context.RequestData.ReadInt64();
+ ulong titleId = context.RequestData.ReadUInt64();
+
+ ulong position = context.Request.ReceiveBuff[0].Position;
+
+ ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
+
+ context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray());
+
+ return ResultCode.Success;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs
new file mode 100644
index 00000000..886bffdd
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/IServiceGetterInterface.cs
@@ -0,0 +1,30 @@
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ [Service("ns:am2")]
+ [Service("ns:ec")]
+ [Service("ns:rid")]
+ [Service("ns:rt")]
+ [Service("ns:web")]
+ class IServiceGetterInterface : IpcService
+ {
+ public IServiceGetterInterface(ServiceCtx context) { }
+
+ [CommandCmif(7996)]
+ // GetApplicationManagerInterface() -> object<nn::ns::detail::IApplicationManagerInterface>
+ public ResultCode GetApplicationManagerInterface(ServiceCtx context)
+ {
+ MakeObject(context, new IApplicationManagerInterface(context));
+
+ return ResultCode.Success;
+ }
+
+ [CommandCmif(7989)]
+ // GetReadOnlyApplicationControlDataInterface() -> object<nn::ns::detail::IReadOnlyApplicationControlDataInterface>
+ public ResultCode GetReadOnlyApplicationControlDataInterface(ServiceCtx context)
+ {
+ MakeObject(context, new IReadOnlyApplicationControlDataInterface(context));
+
+ return ResultCode.Success;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs
new file mode 100644
index 00000000..84ed3d0f
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/ISystemUpdateInterface.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ [Service("ns:su")]
+ class ISystemUpdateInterface : IpcService
+ {
+ public ISystemUpdateInterface(ServiceCtx context) { }
+ }
+} \ No newline at end of file
diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs
new file mode 100644
index 00000000..0b640992
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/Ns/IVulnerabilityManagerInterface.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ns
+{
+ [Service("ns:vm")]
+ class IVulnerabilityManagerInterface : IpcService
+ {
+ public IVulnerabilityManagerInterface(ServiceCtx context) { }
+ }
+} \ No newline at end of file