|
|
@@ -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();
|
|
|
|