diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Ns')
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 |
