From 047dbf3db7f1f694a4db7cad33e32abfb2f00f70 Mon Sep 17 00:00:00 2001 From: Babib3l Date: Mon, 25 May 2026 07:08:31 -0500 Subject: [PATCH] River : HLE: Make process identity explicit for service metadata resolution This PR is the first in a batch of structural changes to Ryujinx. **Changes** - Added `ProcessIdentity` and `ProcessKind` to describe loaded programs by: - PID, program ID, application ID, program index, display version, process kind - Stored identity metadata on `ProcessResult`. - Added PID-based process lookup helpers to `ProcessLoader`. - Updated HLE services to resolve application metadata through the caller PID instead of `Processes.ActiveApplication`. - Added PTC/JIT disk cache initialization logging with PID, title ID, display version, selector, and enabled state. - Added `ClientProcessId` property to ServiceCtx (/src/Ryujinx.HLE/HOS/ServiceCtx.cs) that uses the handle descriptor PId when available, falling back to `Process.Pid`. - Updated 15 HLE service files to use `context.ClientProcessId` instead of `context.Process.Pid` for client process access, ensuring services correctly identify the calling process even when invoked via IPC with handle descriptors. These changes make service metadata resolution more explicit and prepare the emulator for other structural changes later on. --- src/ARMeilleure/Translation/PTC/Ptc.cs | 4 +-- .../HOS/ArmProcessContextFactory.cs | 4 ++- src/Ryujinx.HLE/HOS/ServiceCtx.cs | 1 + .../Acc/IAccountServiceForApplication.cs | 2 +- .../ILibraryAppletProxy.cs | 2 +- .../ILibraryAppletSelfAccessor.cs | 8 +++-- .../ApplicationProxy/IApplicationFunctions.cs | 18 ++++++---- .../IApplicationProxy.cs | 2 +- .../Services/Arp/ApplicationLaunchProperty.cs | 7 +++- .../Caps/IScreenShotApplicationService.cs | 6 ++-- .../HOS/Services/Fatal/IService.cs | 6 ++-- .../HOS/Services/Fs/IFileSystemProxy.cs | 2 +- .../IUserLocalCommunicationService.cs | 10 +++--- .../Services/Ns/Aoc/IAddOnContentManager.cs | 14 ++++---- .../Ns/IApplicationManagerInterface.cs | 2 +- ...ReadOnlyApplicationControlDataInterface.cs | 2 +- .../IParentalControlService.cs | 9 ++--- .../QueryPlayStatisticsManager.cs | 6 ++-- .../Loaders/Processes/ProcessIdentity.cs | 34 +++++++++++++++++++ .../Loaders/Processes/ProcessKind.cs | 12 +++++++ .../Loaders/Processes/ProcessLoader.cs | 17 ++++++++++ .../Loaders/Processes/ProcessLoaderHelper.cs | 1 + .../Loaders/Processes/ProcessResult.cs | 32 +++++++++++++++-- 23 files changed, 155 insertions(+), 46 deletions(-) create mode 100644 src/Ryujinx.HLE/Loaders/Processes/ProcessIdentity.cs create mode 100644 src/Ryujinx.HLE/Loaders/Processes/ProcessKind.cs diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index 47fc98757..5b02da124 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -107,7 +107,7 @@ namespace ARMeilleure.Translation.PTC Profiler.Wait(); Profiler.ClearEntries(); - Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled})."); + Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache v{InternalVersion}\n\t\t (title: {titleIdText}, version: '{displayVersion}', selector: '{cacheSelector}', enabled: {enabled})."); if (!enabled || string.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault) { @@ -126,8 +126,6 @@ namespace ARMeilleure.Translation.PTC DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault; _memoryMode = memoryMode; - Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}"); - string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir); string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir); diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 13f0cb74d..6c8d3cb9e 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -159,7 +159,9 @@ namespace Ryujinx.HLE.HOS } } - DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, _diskCacheSelector); + string cacheSelector = _diskCacheSelector ?? "default"; + + DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, cacheSelector); return processContext; } diff --git a/src/Ryujinx.HLE/HOS/ServiceCtx.cs b/src/Ryujinx.HLE/HOS/ServiceCtx.cs index ba27e12ea..200afaf5e 100644 --- a/src/Ryujinx.HLE/HOS/ServiceCtx.cs +++ b/src/Ryujinx.HLE/HOS/ServiceCtx.cs @@ -16,6 +16,7 @@ namespace Ryujinx.HLE.HOS public IpcMessage Response { get; } public BinaryReader RequestData { get; } public BinaryWriter ResponseData { get; } + public ulong ClientProcessId => Request.HandleDesc.HasPId ? Request.HandleDesc.PId : Process.Pid; public ServiceCtx( Switch device, diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs index 98af10694..759613752 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/IAccountServiceForApplication.cs @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc // TODO: Account actually calls nn::arp::detail::IReader::GetApplicationControlProperty() with the current Pid and store the result (NACP file) internally. // But since we use LibHac and we load one Application at a time, it's not necessary. - context.ResponseData.Write((byte)context.Device.Processes.ActiveApplication.ApplicationControlProperties.UserAccountSwitchLock); + context.ResponseData.Write((byte)context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties.UserAccountSwitchLock); Logger.Stub?.PrintStub(LogClass.ServiceAcc); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs index 7700cac09..cfd35111a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // OpenLibraryAppletSelfAccessor() -> object public ResultCode OpenLibraryAppletSelfAccessor(ServiceCtx context) { - MakeObject(context, new ILibraryAppletSelfAccessor(context)); + MakeObject(context, new ILibraryAppletSelfAccessor(context, _pid)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs index fa986de93..50df65e44 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletProxy/ILibraryAppletSelfAccessor.cs @@ -8,9 +8,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { private readonly AppletStandalone _appletStandalone = new(); - public ILibraryAppletSelfAccessor(ServiceCtx context) + public ILibraryAppletSelfAccessor(ServiceCtx context, ulong pid) { - if (context.Device.Processes.ActiveApplication.ProgramId == 0x0100000000001009) + ulong programId = context.Device.Processes.GetProcess(pid).ProgramId; + + if (programId == 0x0100000000001009) { // Create MiiEdit data. _appletStandalone = new AppletStandalone() @@ -26,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib } else { - throw new NotImplementedException($"{context.Device.Processes.ActiveApplication.ProgramId} applet is not implemented."); + throw new NotImplementedException($"{programId} applet is not implemented."); } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index ef0475e26..097ca535b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -44,8 +44,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati private int _jitLoaded; private readonly LibHac.HorizonClient _horizon; + private readonly ulong _pid; - public IApplicationFunctions(Horizon system) + public IApplicationFunctions(Horizon system, ulong pid) { // TODO: Find where they are signaled. _gpuErrorDetectedSystemEvent = new KEvent(system.KernelContext); @@ -55,6 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati _unknownEvent = new KEvent(system.KernelContext); _horizon = system.LibHacHorizonManager.AmClient; + _pid = pid; } [CommandCmif(1)] @@ -115,11 +117,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode EnsureSaveData(ServiceCtx context) { Uid userId = context.RequestData.ReadStruct().ToLibHacUid(); + var process = context.Device.Processes.GetProcess(_pid); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); + ApplicationId applicationId = new(process.Identity.ApplicationId); - ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty nacp = process.ApplicationControlProperties; LibHac.HorizonClient hos = context.Device.System.LibHacHorizonManager.AmClient; LibHac.Result result = hos.Fs.EnsureApplicationSaveData(out long requiredSize, applicationId, in nacp, in userId); @@ -139,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // TODO: When above calls are implemented, switch to using ns:am long desiredLanguageCode = context.Device.System.State.DesiredLanguageCode; - int supportedLanguages = (int)context.Device.Processes.ActiveApplication.ApplicationControlProperties.SupportedLanguageFlag; + int supportedLanguages = (int)context.Device.Processes.GetProcess(_pid).ApplicationControlProperties.SupportedLanguageFlag; int firstSupported = BitOperations.TrailingZeroCount(supportedLanguages); if (firstSupported > (int)TitleLanguage.BrazilianPortuguese) @@ -182,7 +185,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati public ResultCode GetDisplayVersion(ServiceCtx context) { // If an NACP isn't found, the buffer will be all '\0' which seems to be the correct implementation. - context.ResponseData.Write(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); + context.ResponseData.Write(context.Device.Processes.GetProcess(_pid).ApplicationControlProperties.DisplayVersion); return ResultCode.Success; } @@ -235,11 +238,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati ushort index = (ushort)context.RequestData.ReadUInt64(); long saveSize = context.RequestData.ReadInt64(); long journalSize = context.RequestData.ReadInt64(); + var process = context.Device.Processes.GetProcess(_pid); // Mask out the low nibble of the program ID to get the application ID - ApplicationId applicationId = new(context.Device.Processes.ActiveApplication.ProgramId & ~0xFul); + ApplicationId applicationId = new(process.Identity.ApplicationId); - ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty nacp = process.ApplicationControlProperties; LibHac.Result result = _horizon.Fs.CreateApplicationCacheStorage(out long requiredSize, out CacheStorageTargetMedia storageTarget, applicationId, in nacp, index, saveSize, journalSize); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index b24e1bf4f..27a54a51e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetApplicationFunctions() -> object public ResultCode GetApplicationFunctions(ServiceCtx context) { - MakeObject(context, new IApplicationFunctions(context.Device.System)); + MakeObject(context, new IApplicationFunctions(context.Device.System, _pid)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs b/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs index 26089bddb..b370c3d04 100644 --- a/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs +++ b/src/Ryujinx.HLE/HOS/Services/Arp/ApplicationLaunchProperty.cs @@ -27,13 +27,18 @@ namespace Ryujinx.HLE.HOS.Services.Arp } public static ApplicationLaunchProperty GetByPid(ServiceCtx context) + { + return GetByPid(context, context.ClientProcessId); + } + + public static ApplicationLaunchProperty GetByPid(ServiceCtx context, ulong pid) { // TODO: Handle ApplicationLaunchProperty as array when pid will be supported and return the right item. // For now we can hardcode values, and fix it after GetApplicationLaunchProperty is implemented. return new ApplicationLaunchProperty { - TitleId = context.Device.Processes.ActiveApplication.ProgramId, + TitleId = context.Device.Processes.GetProcess(pid).ProgramId, Version = 0x00, BaseGameStorageId = (byte)StorageId.BuiltInSystem, UpdateGameStorageId = (byte)StorageId.None, diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs index 2ccb7c598..64e9eeca5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/IScreenShotApplicationService.cs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.GetProcess(context.ClientProcessId).ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.GetProcess(context.ClientProcessId).ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); @@ -143,7 +143,7 @@ namespace Ryujinx.HLE.HOS.Services.Caps byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); - ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); + ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.GetProcess(context.ClientProcessId).ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); context.ResponseData.WriteStruct(applicationAlbumEntry); diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index c3ed92ed0..fe1744918 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Fatal.Types; +using Ryujinx.HLE.Loaders.Processes; using System; using System.Runtime.InteropServices; using System.Text; @@ -50,12 +51,13 @@ namespace Ryujinx.HLE.HOS.Services.Fatal private ResultCode ThrowFatalWithCpuContextImpl(ServiceCtx context, ResultCode resultCode, ulong pid, FatalPolicy fatalPolicy, ReadOnlySpan cpuContext) { + ProcessResult process = context.Device.Processes.GetProcess(pid); StringBuilder errorReport = new(); errorReport.AppendLine(); errorReport.AppendLine("ErrorReport log:"); - errorReport.AppendLine($"\tTitleId: {context.Device.Processes.ActiveApplication.ProgramIdText}"); + errorReport.AppendLine($"\tTitleId: {process.ProgramIdText}"); errorReport.AppendLine($"\tPid: {pid}"); errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); @@ -64,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal { errorReport.AppendLine("CPU Context:"); - if (context.Device.Processes.ActiveApplication.Is64Bit) + if (process.Is64Bit) { CpuContext64 cpuContext64 = MemoryMarshal.Cast(cpuContext)[0]; diff --git a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs index 46d28c46a..ae5b3acdf 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fs/IFileSystemProxy.cs @@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs { byte programIndex = context.RequestData.ReadByte(); - if ((context.Device.Processes.ActiveApplication.ProgramId & 0xf) != programIndex) + if (context.Device.Processes.GetProcess(_pid).Identity.ProgramIndex != programIndex) { throw new NotImplementedException($"Accessing storage from other programs is not supported (program index = {programIndex})."); } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 31d8c9710..92dd8671e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator private bool CheckLocalCommunicationIdPermission(ServiceCtx context, ulong localCommunicationIdChecked) { // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty controlProperty = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; foreach (ulong localCommunicationId in controlProperty.LocalCommunicationId) { @@ -423,7 +423,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator if (scanFilter.NetworkId.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId) { // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty controlProperty = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; scanFilter.NetworkId.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0]; } @@ -591,7 +591,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator if (networkConfig.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId) { // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty controlProperty = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; networkConfig.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0]; } @@ -896,7 +896,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator if (networkInfo.NetworkId.IntentId.LocalCommunicationId == -1 && NetworkClient.NeedsRealId) { // TODO: Call nn::arp::GetApplicationControlProperty here when implemented. - ApplicationControlProperty controlProperty = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty controlProperty = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; networkInfo.NetworkId.IntentId.LocalCommunicationId = (long)controlProperty.LocalCommunicationId[0]; } @@ -1138,7 +1138,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator } // TODO: Call nn::arp::GetApplicationLaunchProperty here when implemented. - NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); + NetworkClient.SetGameVersion(context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties.DisplayVersion); resultCode = ResultCode.Success; diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs index c77358803..2cb921500 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/Aoc/IAddOnContentManager.cs @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return CountAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); + return CountAddOnContentImpl(context, context.Device.Processes.GetProcess(pid).ProgramId); } [CommandCmif(3)] @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return ListAddContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); + return ListAddContentImpl(context, context.Device.Processes.GetProcess(pid).ProgramId); } [CommandCmif(4)] // 1.0.0-6.2.0 @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return GetAddOnContentBaseIdImpl(context, context.Device.Processes.ActiveApplication.ProgramId); + return GetAddOnContentBaseIdImpl(context, context.Device.Processes.GetProcess(pid).ProgramId); } [CommandCmif(6)] // 1.0.0-6.2.0 @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // NOTE: Service call arp:r GetApplicationLaunchProperty to get TitleId using the PId. - return PrepareAddOnContentImpl(context, context.Device.Processes.ActiveApplication.ProgramId); + return PrepareAddOnContentImpl(context, context.Device.Processes.GetProcess(pid).ProgramId); } [CommandCmif(8)] // 4.0.0+ @@ -138,7 +138,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // 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); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.GetProcess(pid).ProgramId); if (resultCode != ResultCode.Success) { @@ -310,7 +310,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc // 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; + _addOnContentBaseId = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties.AddOnContentBaseId; if (_addOnContentBaseId == 0) { @@ -324,7 +324,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns.Aoc { uint index = context.RequestData.ReadUInt32(); - ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, context.Device.Processes.ActiveApplication.ProgramId); + ResultCode resultCode = GetAddOnContentBaseIdFromTitleId(context, titleId); if (resultCode != ResultCode.Success) { diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index f510da594..a45054aba 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns ulong position = context.Request.ReceiveBuff[0].Position; - ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty nacp = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs index ca7d42b48..971929298 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IReadOnlyApplicationControlDataInterface.cs @@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns ulong position = context.Request.ReceiveBuff[0].Position; - ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ApplicationControlProperty nacp = context.Device.Processes.GetProcess(context.ClientProcessId).ApplicationControlProperties; context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); diff --git a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs index c6e89b417..4c0fe9d6e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Pctl/ParentalControlServiceFactory/IParentalControlService.cs @@ -48,21 +48,22 @@ namespace Ryujinx.HLE.HOS.Services.Pctl.ParentalControlServiceFactory { if ((_permissionFlag & 0x40) == 0) { - ulong titleId = ApplicationLaunchProperty.GetByPid(context).TitleId; + ulong titleId = ApplicationLaunchProperty.GetByPid(context, _pid).TitleId; if (titleId != 0) { _titleId = titleId; + var process = context.Device.Processes.GetProcess(_pid); // TODO: Call nn::arp::GetApplicationControlProperty here when implemented, if it return ResultCode.Success we assign fields. - _ratingAge = new int[context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge.Length]; + _ratingAge = new int[process.ApplicationControlProperties.RatingAge.Length]; for (int i = 0; i < _ratingAge.Length; i++) { - _ratingAge[i] = Convert.ToInt32(context.Device.Processes.ActiveApplication.ApplicationControlProperties.RatingAge[i]); + _ratingAge[i] = Convert.ToInt32(process.ApplicationControlProperties.RatingAge[i]); } - _parentalControlFlag = context.Device.Processes.ActiveApplication.ApplicationControlProperties.ParentalControlFlag; + _parentalControlFlag = process.ApplicationControlProperties.ParentalControlFlag; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs index 74729c70a..2687d05b1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pdm/QueryService/QueryPlayStatisticsManager.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService.Types; +using Ryujinx.HLE.Loaders.Processes; using System; using System.Collections.Generic; using System.Linq; @@ -31,7 +32,8 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService } } - PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryCapability; + ProcessResult process = context.Device.Processes.GetProcess(context.ClientProcessId); + PlayLogQueryCapability queryCapability = (PlayLogQueryCapability)process.ApplicationControlProperties.PlayLogQueryCapability; List titleIds = []; @@ -45,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService // Check if input title ids are in the whitelist. foreach (ulong titleId in titleIds) { - if (!context.Device.Processes.ActiveApplication.ApplicationControlProperties.PlayLogQueryableApplicationId.AsReadOnlySpan().Contains(titleId)) + if (!process.ApplicationControlProperties.PlayLogQueryableApplicationId.AsReadOnlySpan().Contains(titleId)) { return (ResultCode)Am.ResultCode.ObjectInvalid; } diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessIdentity.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessIdentity.cs new file mode 100644 index 000000000..051e9a18f --- /dev/null +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessIdentity.cs @@ -0,0 +1,34 @@ +namespace Ryujinx.HLE.Loaders.Processes +{ + public readonly struct ProcessIdentity + { + public ulong ProcessId { get; } + public ulong ProgramId { get; } + public ulong ApplicationId { get; } + public byte ProgramIndex { get; } + public string ProgramIdText { get; } + public string DisplayVersion { get; } + public ProcessKind Kind { get; } + + public ProcessIdentity( + ulong processId, + ulong programId, + byte programIndex, + string displayVersion, + ProcessKind kind) + { + ProcessId = processId; + ProgramId = programId; + ProgramIndex = programIndex; + ApplicationId = programId & ~0xFul; + ProgramIdText = $"{programId:x16}"; + DisplayVersion = displayVersion ?? string.Empty; + Kind = kind; + } + + public override string ToString() + { + return $"{Kind} pid={ProcessId} program={ProgramIdText} application={ApplicationId:x16} index={ProgramIndex} version={DisplayVersion}"; + } + } +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessKind.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessKind.cs new file mode 100644 index 000000000..eda7c9e86 --- /dev/null +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessKind.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.Loaders.Processes +{ + public enum ProcessKind + { + Unknown, + Application, + SystemApplication, + SystemApplet, + LibraryApplet, + Homebrew, + } +} diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index f7427c690..c7d3a499d 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -32,6 +32,23 @@ namespace Ryujinx.HLE.Loaders.Processes _processesByPid = new ConcurrentDictionary(); } + public bool TryGetProcess(ulong pid, out ProcessResult process) + { + return _processesByPid.TryGetValue(pid, out process); + } + + public ProcessResult GetProcess(ulong pid) + { + if (_processesByPid.TryGetValue(pid, out ProcessResult process)) + { + return process; + } + + Logger.Warning?.Print(LogClass.Loader, $"Process metadata for pid {pid} was not found. Falling back to active application metadata."); + + return ActiveApplication; + } + public bool LoadXci(string path, ulong applicationId) { FileStream stream = new(path, FileMode.Open, FileAccess.Read); diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index f58edcabf..498b6bb7c 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -456,6 +456,7 @@ namespace Ryujinx.HLE.Loaders.Processes allowCodeMemoryForJit, processContextFactory.DiskCacheLoadState, process.Pid, + programIndex, meta.MainThreadPriority, meta.MainThreadStackSize, device.System.State.DesiredTitleLanguage); diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 1b40ab519..279268de6 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -11,7 +11,7 @@ namespace Ryujinx.HLE.Loaders.Processes { public class ProcessResult { - public static ProcessResult Failed => new(null, new BlitStruct(1), false, false, null, 0, 0, 0, TitleLanguage.AmericanEnglish); + public static ProcessResult Failed => new(null, new BlitStruct(1), false, false, null, 0, 0, 0, 0, TitleLanguage.AmericanEnglish); private readonly byte _mainThreadPriority; private readonly uint _mainThreadStackSize; @@ -29,6 +29,7 @@ namespace Ryujinx.HLE.Loaders.Processes public readonly bool Is64Bit; public readonly bool DiskCacheEnabled; public readonly bool AllowCodeMemoryForJit; + public readonly ProcessIdentity Identity; public ProcessResult( MetaLoader metaLoader, @@ -37,6 +38,7 @@ namespace Ryujinx.HLE.Loaders.Processes bool allowCodeMemoryForJit, IDiskCacheLoadState diskCacheLoadState, ulong pid, + byte programIndex, byte mainThreadPriority, uint mainThreadStackSize, TitleLanguage titleLanguage) @@ -72,8 +74,8 @@ namespace Ryujinx.HLE.Loaders.Processes ProgramId = programId; ProgramIdText = $"{programId:x16}"; Is64Bit = metaLoader.IsProgram64Bit; + Identity = new ProcessIdentity(pid, programId, programIndex, DisplayVersion, GetProcessKind(programId)); } - else { Logger.Error?.Print(LogClass.Application,$"metaLoader is null !!!"); @@ -85,6 +87,31 @@ namespace Ryujinx.HLE.Loaders.Processes AllowCodeMemoryForJit = allowCodeMemoryForJit; } + private static ProcessKind GetProcessKind(ulong programId) + { + if (programId == 0) + { + return ProcessKind.Application; + } + + if (programId is >= 0x0100000000001000 and <= 0x0100000000001FFF) + { + return ProcessKind.SystemApplet; + } + + if (programId is >= 0x0100000000000800 and <= 0x0100000000000FFF) + { + return ProcessKind.LibraryApplet; + } + + if (programId <= 0x0100000000007FFF) + { + return ProcessKind.SystemApplication; + } + + return ProcessKind.Application; + } + public bool Start(Switch device) { device.Configuration.ContentManager.LoadEntries(device); @@ -101,6 +128,7 @@ namespace Ryujinx.HLE.Loaders.Processes string version = ProgramId > 0x0100000000007FFF ? DisplayVersion : device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?"; Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); + Logger.Info?.Print(LogClass.Loader, $"Process identity: {Identity}"); return true; }