1
0
Fork 0
mirror of https://github.com/Ryujinx/Ryujinx.git synced 2025-03-01 22:36:07 +00:00

aoc/am: Cleanup aoc service and stub am calls ()

* aoc/am: Cleanup aoc service and stub am calls

This PR implement aoc call `GetAddOnContentListChangedEventWithProcessId` (Closes ) and `CreateContentsServiceManager`. Additionnally, a big cleanup (checked by RE on latest firmware) is made on the whole service. I've added `CountAddOnContent`, `ListAddOnContent` and `GetAddonContentBaseId` for games which require version `1.0.0-6.2.0` too.

Am service call `ReportUserIsActive` is stubbed (checked by RE, closes ).

Since some logic in the service (aoc) which handle the DLCs has been changed, it could be nice to have some testing to be sure there is no regression.

* Remove wrong check

* Addresses gdkchan feedback

* Fix GetAddOnContentLostErrorCode

* fix null pid in services

* Add missing comment

* remove leftover comment
This commit is contained in:
Ac_K 2021-06-29 18:57:06 +02:00 committed by GitHub
parent 28618c58d7
commit 8cc872fb60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 332 additions and 206 deletions
Ryujinx.HLE/HOS/Services

View file

@ -285,6 +285,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandHipc(65)]
// ReportUserIsActive()
public ResultCode ReportUserIsActive(ServiceCtx context)
{
// TODO: Call idle:sys ReportUserIsActive when implemented.
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandHipc(68)]
// SetAutoSleepDisabled(u8)
public ResultCode SetAutoSleepDisabled(ServiceCtx context)

View file

@ -279,7 +279,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
// Pid placeholder
context.RequestData.ReadInt64();
long pid = context.Process.Pid;
long pid = context.Request.HandleDesc.PId;
ulong playHistoryRegistrationKeyPosition = context.Request.PtrBuff[0].Position;
ulong PlayHistoryRegistrationKeySize = context.Request.PtrBuff[0].Size;

View file

@ -0,0 +1,285 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
{
[Service("aoc:u")]
class IAddOnContentManager : IpcService
{
private readonly KEvent _addOnContentListChangedEvent;
private ulong _addOnContentBaseId;
public IAddOnContentManager(ServiceCtx context)
{
_addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext);
}
[CommandHipc(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);
}
[CommandHipc(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);
}
[CommandHipc(2)]
// CountAddOnContent(pid) -> u32
public ResultCode CountAddOnContent(ServiceCtx context)
{
long pid = context.Request.HandleDesc.PId;
// NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
return CountAddOnContentImpl(context, context.Device.Application.TitleId);
}
[CommandHipc(3)]
// ListAddOnContent(u32 start_index, u32 buffer_size, pid) -> (u32 count, buffer<u32>)
public ResultCode ListAddOnContent(ServiceCtx context)
{
long pid = context.Request.HandleDesc.PId;
// NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
return ListAddContentImpl(context, context.Device.Application.TitleId);
}
[CommandHipc(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);
}
[CommandHipc(5)]
// GetAddOnContentBaseId(pid) -> u64
public ResultCode GetAddOnContentBaseId(ServiceCtx context)
{
long pid = context.Request.HandleDesc.PId;
// NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
return GetAddOnContentBaseIdImpl(context, context.Device.Application.TitleId);
}
[CommandHipc(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);
}
[CommandHipc(7)]
// PrepareAddOnContent(u32 index, pid)
public ResultCode PrepareAddOnContent(ServiceCtx context)
{
long pid = context.Request.HandleDesc.PId;
// NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId.
return PrepareAddOnContentImpl(context, context.Device.Application.TitleId);
}
[CommandHipc(8)] // 4.0.0+
// GetAddOnContentListChangedEvent() -> handle<copy>
public ResultCode GetAddOnContentListChangedEvent(ServiceCtx context)
{
return GetAddOnContentListChangedEventImpl(context);
}
[CommandHipc(9)] // 10.0.0+
// GetAddOnContentLostErrorCode() -> u64
public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context)
{
// NOTE: 0x7D0A4 -> 2164-1000
context.ResponseData.Write(GetAddOnContentLostErrorCodeImpl(0x7D0A4));
return ResultCode.Success;
}
[CommandHipc(10)] // 11.0.0+
// GetAddOnContentListChangedEventWithProcessId(pid) -> handle<copy>
public ResultCode GetAddOnContentListChangedEventWithProcessId(ServiceCtx context)
{
long 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.Application.TitleId);
if (resultCode != ResultCode.Success)
{
return resultCode;
}
return GetAddOnContentListChangedEventImpl(context);
}
[CommandHipc(100)] // 7.0.0+
// CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
public ResultCode CreateEcPurchasedEventManager(ServiceCtx context)
{
MakeObject(context, new IPurchaseEventManager(context.Device.System));
return ResultCode.Success;
}
[CommandHipc(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;
}
[CommandHipc(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 bufferSize = context.RequestData.ReadUInt32();
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
// TODO: This should use _addOnContentBaseId;
uint aocCount = (uint)context.Device.System.ContentManager.GetAocCount();
if (aocCount - startIndex > bufferSize)
{
return ResultCode.InvalidBufferSize;
}
if (aocCount <= startIndex)
{
context.ResponseData.Write(0);
return ResultCode.Success;
}
IList<ulong> aocTitleIds = context.Device.System.ContentManager.GetAocTitleIds();
GetAddOnContentBaseIdFromTitleId(context, titleId);
for (int i = 0; i < aocCount; i++)
{
context.Memory.Write(bufferPosition + (ulong)i * 4, (uint)(aocTitleIds[i + (int)startIndex] - _addOnContentBaseId));
}
context.ResponseData.Write(aocCount);
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.Application.ControlData.Value.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.Application.TitleId);
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 (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out int addOnContentListChangedEventHandle) != KernelResult.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;
}
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
{
class IContentsServiceManager : IpcService
{
public IContentsServiceManager() { }
}
}

View file

@ -1,11 +1,10 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Services.Ns
namespace Ryujinx.HLE.HOS.Services.Ns.Aoc
{
class IPurchaseEventManager : IpcService
{
@ -20,8 +19,8 @@ namespace Ryujinx.HLE.HOS.Services.Ns
// 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;
ulong inBufferPosition = context.Request.SendBuff[0].Position;
ulong inBufferSize = context.Request.SendBuff[0].Size;
byte[] buffer = new byte[inBufferSize];
context.Memory.Read(inBufferPosition, buffer);
@ -39,12 +38,12 @@ namespace Ryujinx.HLE.HOS.Services.Ns
// GetPurchasedEventReadableHandle() -> handle<copy, event>
public ResultCode GetPurchasedEventReadableHandle(ServiceCtx context)
{
if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int handle) != KernelResult.Success)
if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int purchasedEventReadableHandle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(purchasedEventReadableHandle);
return ResultCode.Success;
}

View file

@ -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
}
}

View file

@ -1,193 +0,0 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Services.Ns
{
[Service("aoc:u")]
class IAddOnContentManager : IpcService
{
private readonly KEvent _addOnContentListChangedEvent;
private int _addOnContentListChangedEventHandle;
public IAddOnContentManager(ServiceCtx context)
{
_addOnContentListChangedEvent = new KEvent(context.Device.System.KernelContext);
}
[CommandHipc(2)]
// CountAddOnContent(pid) -> u32
public ResultCode CountAddOnContent(ServiceCtx context)
{
long pid = context.Process.Pid;
// Official code checks ApplicationControlProperty.RuntimeAddOnContentInstall
// if true calls ns:am ListAvailableAddOnContent again to get updated count
byte runtimeAddOnContentInstall = context.Device.Application.ControlData.Value.RuntimeAddOnContentInstall;
if (runtimeAddOnContentInstall != 0)
{
Logger.Warning?.Print(LogClass.ServiceNs, $"RuntimeAddOnContentInstall is true. Some DLC may be missing");
}
uint aocCount = CountAddOnContentImpl(context);
context.ResponseData.Write(aocCount);
Logger.Stub?.PrintStub(LogClass.ServiceNs, new { aocCount, runtimeAddOnContentInstall });
return ResultCode.Success;
}
private static uint CountAddOnContentImpl(ServiceCtx context)
{
return (uint)context.Device.System.ContentManager.GetAocCount();
}
[CommandHipc(3)]
// ListAddOnContent(u32, u32, pid) -> (u32, buffer<u32>)
public ResultCode ListAddOnContent(ServiceCtx context)
{
uint startIndex = context.RequestData.ReadUInt32();
uint bufferSize = context.RequestData.ReadUInt32();
long pid = context.Process.Pid;
var aocTitleIds = context.Device.System.ContentManager.GetAocTitleIds();
uint aocCount = CountAddOnContentImpl(context);
if (aocCount <= startIndex)
{
context.ResponseData.Write((uint)0);
return ResultCode.Success;
}
aocCount = Math.Min(aocCount - startIndex, bufferSize);
context.ResponseData.Write(aocCount);
ulong bufAddr = (ulong)context.Request.ReceiveBuff[0].Position;
ulong aocBaseId = GetAddOnContentBaseIdImpl(context);
for (int i = 0; i < aocCount; ++i)
{
context.Memory.Write(bufAddr + (ulong)i * 4, (int)(aocTitleIds[i + (int)startIndex] - aocBaseId));
}
Logger.Stub?.PrintStub(LogClass.ServiceNs, new { bufferSize, startIndex, aocCount });
return ResultCode.Success;
}
[CommandHipc(5)]
// GetAddOnContentBaseId(pid) -> u64
public ResultCode GetAddonContentBaseId(ServiceCtx context)
{
long pid = context.Process.Pid;
// Official code calls arp:r GetApplicationControlProperty to get AddOnContentBaseId
// If the call fails, calls arp:r GetApplicationLaunchProperty to get App TitleId
ulong aocBaseId = GetAddOnContentBaseIdImpl(context);
context.ResponseData.Write(aocBaseId);
Logger.Stub?.PrintStub(LogClass.ServiceNs, $"aocBaseId={aocBaseId:X16}");
// ResultCode will be error code of GetApplicationLaunchProperty if it fails
return ResultCode.Success;
}
private static ulong GetAddOnContentBaseIdImpl(ServiceCtx context)
{
ulong aocBaseId = context.Device.Application.ControlData.Value.AddOnContentBaseId;
if (aocBaseId == 0)
{
aocBaseId = context.Device.Application.TitleId + 0x1000;
}
return aocBaseId;
}
[CommandHipc(7)]
// PrepareAddOnContent(u32, pid)
public ResultCode PrepareAddOnContent(ServiceCtx context)
{
uint aocIndex = context.RequestData.ReadUInt32();
long pid = context.Process.Pid;
// Official Code calls a bunch of functions from arp:r for aocBaseId
// and ns:am RegisterContentsExternalKey?, GetOwnedApplicationContentMetaStatus? etc...
// Ideally, this should probably initialize the AocData values for the specified index
Logger.Stub?.PrintStub(LogClass.ServiceNs, new { aocIndex });
return ResultCode.Success;
}
[CommandHipc(8)]
// GetAddOnContentListChangedEvent() -> handle<copy>
public ResultCode GetAddOnContentListChangedEvent(ServiceCtx context)
{
// Official code seems to make an internal call to ns:am Cmd 84 GetDynamicCommitEvent()
if (_addOnContentListChangedEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_addOnContentListChangedEvent.ReadableEvent, out _addOnContentListChangedEventHandle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_addOnContentListChangedEventHandle);
Logger.Stub?.PrintStub(LogClass.ServiceNs);
return ResultCode.Success;
}
[CommandHipc(9)] // [10.0.0+]
// GetAddOnContentLostErrorCode() -> u64
public ResultCode GetAddOnContentLostErrorCode(ServiceCtx context)
{
// Seems to calculate ((value & 0x1ff)) + 2000 on 0x7d0a4
// which gives 0x874 (2000+164). 164 being Module ID of `EC (Shop)`
context.ResponseData.Write(2164L);
Logger.Stub?.PrintStub(LogClass.ServiceNs);
return ResultCode.Success;
}
[CommandHipc(100)]
// CreateEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
public ResultCode CreateEcPurchasedEventManager(ServiceCtx context)
{
MakeObject(context, new IPurchaseEventManager(context.Device.System));
Logger.Stub?.PrintStub(LogClass.ServiceNs);
return ResultCode.Success;
}
[CommandHipc(101)]
// CreatePermanentEcPurchasedEventManager() -> object<nn::ec::IPurchaseEventManager>
public ResultCode CreatePermanentEcPurchasedEventManager(ServiceCtx context)
{
// Very similar to CreateEcPurchasedEventManager but with some extra code
MakeObject(context, new IPurchaseEventManager(context.Device.System));
Logger.Stub?.PrintStub(LogClass.ServiceNs);
return ResultCode.Success;
}
}
}

View file

@ -19,8 +19,9 @@ namespace Ryujinx.HLE.HOS.Services.Pctl
// CreateService(u64, pid) -> object<nn::pctl::detail::ipc::IParentalControlService>
public ResultCode CreateService(ServiceCtx context)
{
// TODO: Should pass the pid.
MakeObject(context, new IParentalControlService(context, true, _permissionFlag));
long pid = context.Request.HandleDesc.PId;
MakeObject(context, new IParentalControlService(context, pid, true, _permissionFlag));
return ResultCode.Success;
}
@ -29,8 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.Pctl
// CreateServiceWithoutInitialize(u64, pid) -> object<nn::pctl::detail::ipc::IParentalControlService>
public ResultCode CreateServiceWithoutInitialize(ServiceCtx context)
{
// TODO: Should pass the pid.
MakeObject(context, new IParentalControlService(context, false, _permissionFlag));
long pid = context.Request.HandleDesc.PId;
MakeObject(context, new IParentalControlService(context, pid, false, _permissionFlag));
return ResultCode.Success;
}

View file

@ -7,6 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
{
class IParentalControlService : IpcService
{
private long _pid;
private int _permissionFlag;
private ulong _titleId;
private ParentalControlFlagValue _parentalControlFlag;
@ -18,8 +19,9 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
private bool _stereoVisionRestrictionConfigurable = true;
private bool _stereoVisionRestriction = false;
public IParentalControlService(ServiceCtx context, bool withInitialize, int permissionFlag)
public IParentalControlService(ServiceCtx context, long pid, bool withInitialize, int permissionFlag)
{
_pid = pid;
_permissionFlag = permissionFlag;
if (withInitialize)
@ -39,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory
ResultCode resultCode = ResultCode.InvalidPid;
if (context.Process.Pid != 0)
if (_pid != 0)
{
if ((_permissionFlag & 0x40) == 0)
{