aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/ApplicationLoader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/HOS/ApplicationLoader.cs')
-rw-r--r--Ryujinx.HLE/HOS/ApplicationLoader.cs103
1 files changed, 96 insertions, 7 deletions
diff --git a/Ryujinx.HLE/HOS/ApplicationLoader.cs b/Ryujinx.HLE/HOS/ApplicationLoader.cs
index b3d23ea7..50ab9e1f 100644
--- a/Ryujinx.HLE/HOS/ApplicationLoader.cs
+++ b/Ryujinx.HLE/HOS/ApplicationLoader.cs
@@ -24,6 +24,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text;
using static LibHac.Fs.ApplicationSaveDataManagement;
using static Ryujinx.HLE.HOS.ModLoader;
@@ -308,6 +309,94 @@ namespace Ryujinx.HLE.HOS
LoadNca(nca, null, null);
}
+ public void LoadServiceNca(string ncaFile)
+ {
+ // Disable PPTC here as it does not support multiple processes running.
+ // TODO: This should be eventually removed and it should stop using global state and
+ // instead manage the cache per process.
+ Ptc.Close();
+ PtcProfiler.Stop();
+
+ FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
+ Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
+
+ if (mainNca.Header.ContentType != NcaContentType.Program)
+ {
+ Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
+
+ return;
+ }
+
+ IFileSystem codeFs = null;
+
+ if (mainNca.CanOpenSection(NcaSectionType.Code))
+ {
+ codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
+ }
+
+ if (codeFs == null)
+ {
+ Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
+
+ return;
+ }
+
+ using var npdmFile = new UniqueRef<IFile>();
+
+ Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
+
+ MetaLoader metaData;
+
+ npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
+
+ var npdmBuffer = new byte[fileSize];
+ npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
+
+ metaData = new MetaLoader();
+ metaData.Load(npdmBuffer).ThrowIfFailure();
+
+ NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
+
+ for (int i = 0; i < nsos.Length; i++)
+ {
+ string name = ExeFsPrefixes[i];
+
+ if (!codeFs.FileExists($"/{name}"))
+ {
+ continue; // File doesn't exist, skip.
+ }
+
+ Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
+
+ using var nsoFile = new UniqueRef<IFile>();
+
+ codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+
+ nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
+ }
+
+ // Collect the nsos, ignoring ones that aren't used.
+ NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
+
+ MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
+
+ if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
+ {
+ memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
+ }
+
+ metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
+ ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
+ ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
+
+ string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
+ bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
+
+ string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0');
+
+ Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
+ }
+
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
{
if (mainNca.Header.ContentType != NcaContentType.Program)
@@ -508,7 +597,7 @@ namespace Ryujinx.HLE.HOS
{
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{
- metaData = null; //TODO: Check if we should retain old npdm
+ metaData = null; // TODO: Check if we should retain old npdm.
}
metaData ??= ReadNpdm(codeFs);
@@ -521,7 +610,7 @@ namespace Ryujinx.HLE.HOS
if (!codeFs.FileExists($"/{name}"))
{
- continue; // file doesn't exist, skip
+ continue; // File doesn't exist, skip.
}
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
@@ -533,13 +622,13 @@ namespace Ryujinx.HLE.HOS
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
}
- // ExeFs file replacements
+ // ExeFs file replacements.
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
- // collect the nsos, ignoring ones that aren't used
+ // Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
- // take the npdm from mods if present
+ // Take the npdm from mods if present.
if (modLoadResult.Npdm != null)
{
metaData = modLoadResult.Npdm;
@@ -598,7 +687,7 @@ namespace Ryujinx.HLE.HOS
executable = obj;
- // homebrew NRO can actually have some data after the actual NRO
+ // Homebrew NRO can actually have some data after the actual NRO.
if (input.Length > obj.FileSize)
{
input.Position = obj.FileSize;
@@ -677,7 +766,7 @@ namespace Ryujinx.HLE.HOS
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
- // Explicitly null titleid to disable the shader cache
+ // Explicitly null titleid to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set();