aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE')
-rw-r--r--src/Ryujinx.HLE/FileSystem/ContentManager.cs42
-rw-r--r--src/Ryujinx.HLE/FileSystem/ContentMetaData.cs61
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs1
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs86
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs133
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs8
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs9
-rw-r--r--src/Ryujinx.HLE/Switch.cs8
-rw-r--r--src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs45
9 files changed, 259 insertions, 134 deletions
diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
index 3c34a886..e6c0fce0 100644
--- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs
+++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs
@@ -14,6 +14,7 @@ using Ryujinx.Common.Utilities;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Services.Ssl;
using Ryujinx.HLE.HOS.Services.Time;
+using Ryujinx.HLE.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
@@ -184,41 +185,6 @@ namespace Ryujinx.HLE.FileSystem
}
}
- // fs must contain AOC nca files in its root
- public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel)
- {
- _virtualFileSystem.ImportTickets(fs);
-
- foreach (var ncaPath in fs.EnumerateEntries("*.cnmt.nca", SearchOptions.Default))
- {
- using var ncaFile = new UniqueRef<IFile>();
-
- fs.OpenFile(ref ncaFile.Ref, ncaPath.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
- var nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
- if (nca.Header.ContentType != NcaContentType.Meta)
- {
- Logger.Warning?.Print(LogClass.Application, $"{ncaPath} is not a valid metadata file");
-
- continue;
- }
-
- using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel);
- using var cnmtFile = new UniqueRef<IFile>();
-
- pfs0.OpenFile(ref cnmtFile.Ref, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
-
- var cnmt = new Cnmt(cnmtFile.Get.AsStream());
- if (cnmt.Type != ContentMetaType.AddOnContent || (cnmt.TitleId & 0xFFFFFFFFFFFFE000) != aocBaseId)
- {
- continue;
- }
-
- string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower();
-
- AddAocItem(cnmt.TitleId, containerPath, $"/{ncaId}.nca", true);
- }
- }
-
public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false)
{
// TODO: Check Aoc version.
@@ -232,11 +198,7 @@ namespace Ryujinx.HLE.FileSystem
if (!mergedToContainer)
{
- using FileStream fileStream = File.OpenRead(containerPath);
- using PartitionFileSystem partitionFileSystem = new();
- partitionFileSystem.Initialize(fileStream.AsStorage()).ThrowIfFailure();
-
- _virtualFileSystem.ImportTickets(partitionFileSystem);
+ using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerPath, _virtualFileSystem);
}
}
}
diff --git a/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs b/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs
new file mode 100644
index 00000000..aebcf798
--- /dev/null
+++ b/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs
@@ -0,0 +1,61 @@
+using LibHac.Common.Keys;
+using LibHac.Fs.Fsa;
+using LibHac.Ncm;
+using LibHac.Tools.FsSystem.NcaUtils;
+using LibHac.Tools.Ncm;
+using Ryujinx.HLE.Loaders.Processes.Extensions;
+using System;
+
+namespace Ryujinx.HLE.FileSystem
+{
+ /// <summary>
+ /// Thin wrapper around <see cref="Cnmt"/>
+ /// </summary>
+ public class ContentMetaData
+ {
+ private readonly IFileSystem _pfs;
+ private readonly Cnmt _cnmt;
+
+ public ulong Id => _cnmt.TitleId;
+ public TitleVersion Version => _cnmt.TitleVersion;
+ public ContentMetaType Type => _cnmt.Type;
+ public ulong ApplicationId => _cnmt.ApplicationTitleId;
+ public ulong PatchId => _cnmt.PatchTitleId;
+ public TitleVersion RequiredSystemVersion => _cnmt.MinimumSystemVersion;
+ public TitleVersion RequiredApplicationVersion => _cnmt.MinimumApplicationVersion;
+ public byte[] Digest => _cnmt.Hash;
+
+ public ulong ProgramBaseId => Id & ~0x1FFFUL;
+ public bool IsSystemTitle => _cnmt.Type < ContentMetaType.Application;
+
+ public ContentMetaData(IFileSystem pfs, Cnmt cnmt)
+ {
+ _pfs = pfs;
+ _cnmt = cnmt;
+ }
+
+ public Nca GetNcaByType(KeySet keySet, ContentType type, int programIndex = 0)
+ {
+ // TODO: Replace this with a check for IdOffset as soon as LibHac supports it:
+ // && entry.IdOffset == programIndex
+
+ foreach (var entry in _cnmt.ContentEntries)
+ {
+ if (entry.Type != type)
+ {
+ continue;
+ }
+
+ string ncaId = BitConverter.ToString(entry.NcaId).Replace("-", null).ToLower();
+ Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca");
+
+ if (nca.GetProgramIndex() == programIndex)
+ {
+ return nca;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
index 6c2415e4..6c2a1989 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs
@@ -3,7 +3,6 @@ using LibHac.FsSystem;
using LibHac.Loader;
using LibHac.Ncm;
using LibHac.Ns;
-using Ryujinx.HLE.HOS;
using Ryujinx.HLE.Loaders.Processes.Extensions;
namespace Ryujinx.HLE.Loaders.Processes
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
index e70fcb6f..da563720 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
@@ -7,16 +7,25 @@ using LibHac.Ncm;
using LibHac.Ns;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
+using LibHac.Tools.Ncm;
+using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
+using Ryujinx.HLE.Utilities;
using System.IO;
using System.Linq;
using ApplicationId = LibHac.Ncm.ApplicationId;
+using ContentType = LibHac.Ncm.ContentType;
+using Path = System.IO.Path;
namespace Ryujinx.HLE.Loaders.Processes.Extensions
{
- static class NcaExtensions
+ public static class NcaExtensions
{
+ private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
+
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
{
// Extract RomFs and ExeFs from NCA.
@@ -47,7 +56,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
nacpData = controlNca.GetNacp(device);
}
- /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant update.
+ /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update.
// Load program 0 control NCA as we are going to need it for display version.
(_, Nca updateProgram0ControlNca) = GetGameUpdateData(_device.Configuration.VirtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
@@ -86,6 +95,11 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return processResult;
}
+ public static ulong GetProgramIdBase(this Nca nca)
+ {
+ return nca.Header.TitleId & ~0x1FFFUL;
+ }
+
public static int GetProgramIndex(this Nca nca)
{
return (int)(nca.Header.TitleId & 0xF);
@@ -96,6 +110,11 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return nca.Header.ContentType == NcaContentType.Program;
}
+ public static bool IsMain(this Nca nca)
+ {
+ return nca.IsProgram() && !nca.IsPatch();
+ }
+
public static bool IsPatch(this Nca nca)
{
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
@@ -108,6 +127,43 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return nca.Header.ContentType == NcaContentType.Control;
}
+ public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath)
+ {
+ updatePath = null;
+
+ // Load Update NCAs.
+ Nca updatePatchNca = null;
+ Nca updateControlNca = null;
+
+ // Clear the program index part.
+ ulong titleIdBase = mainNca.GetProgramIdBase();
+
+ // Load update information if exists.
+ string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
+ if (File.Exists(titleUpdateMetadataPath))
+ {
+ updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
+ if (File.Exists(updatePath))
+ {
+ IFileSystem updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem);
+
+ foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel))
+ {
+ if ((applicationTitleId & ~0x1FFFUL) != titleIdBase)
+ {
+ continue;
+ }
+
+ updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex);
+ updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
+ break;
+ }
+ }
+ }
+
+ return (updatePatchNca, updateControlNca);
+ }
+
public static IFileSystem GetExeFs(this Nca nca, Switch device, Nca patchNca = null)
{
IFileSystem exeFs = null;
@@ -172,5 +228,31 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return nacpData;
}
+
+ public static Cnmt GetCnmt(this Nca cnmtNca, IntegrityCheckLevel checkLevel, ContentMetaType metaType)
+ {
+ string path = $"/{metaType}_{cnmtNca.Header.TitleId:x16}.cnmt";
+ using var cnmtFile = new UniqueRef<IFile>();
+
+ try
+ {
+ Result result = cnmtNca.OpenFileSystem(0, checkLevel)
+ .OpenFile(ref cnmtFile.Ref, path.ToU8Span(), OpenMode.Read);
+
+ if (result.IsSuccess())
+ {
+ return new Cnmt(cnmtFile.Release().AsStream());
+ }
+ }
+ catch (HorizonResultException ex)
+ {
+ if (!ResultFs.PathNotFound.Includes(ex.ResultValue))
+ {
+ Logger.Warning?.Print(LogClass.Application, $"Failed get CNMT for '{cnmtNca.Header.TitleId:x16}' from NCA: {ex.Message}");
+ }
+ }
+
+ return null;
+ }
}
}
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
index e95b1b05..bee2572a 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
@@ -1,26 +1,58 @@
using LibHac.Common;
+using LibHac.Common.Keys;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
+using LibHac.Ncm;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
+using LibHac.Tools.Ncm;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.FileSystem;
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
+using ContentType = LibHac.Ncm.ContentType;
namespace Ryujinx.HLE.Loaders.Processes.Extensions
{
public static class PartitionFileSystemExtensions
{
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
- private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
- internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, out string errorMessage)
+ public static Dictionary<ulong, ContentMetaData> GetContentData(this IFileSystem partitionFileSystem,
+ ContentMetaType contentType, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
+ {
+ fileSystem.ImportTickets(partitionFileSystem);
+
+ var programs = new Dictionary<ulong, ContentMetaData>();
+
+ foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
+ {
+ Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, contentType);
+
+ if (cnmt == null)
+ {
+ continue;
+ }
+
+ ContentMetaData content = new(partitionFileSystem, cnmt);
+
+ if (content.Type != contentType)
+ {
+ continue;
+ }
+
+ programs.TryAdd(content.ApplicationId, content);
+ }
+
+ return programs;
+ }
+
+ internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, ulong applicationId, out string errorMessage)
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
where TFormat : IPartitionFileSystemFormat
where THeader : unmanaged, IPartitionFileSystemHeader
@@ -35,31 +67,22 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
try
{
- device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem);
+ Dictionary<ulong, ContentMetaData> applications = partitionFileSystem.GetContentData(ContentMetaType.Application, device.FileSystem, device.System.FsIntegrityCheckLevel);
- // TODO: To support multi-games container, this should use CNMT NCA instead.
- foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
+ if (applicationId == 0)
{
- Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
-
- if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
- {
- continue;
- }
-
- if (nca.IsPatch())
+ foreach ((ulong _, ContentMetaData content) in applications)
{
- patchNca = nca;
- }
- else if (nca.IsProgram())
- {
- mainNca = nca;
- }
- else if (nca.IsControl())
- {
- controlNca = nca;
+ mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
+ controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
+ break;
}
}
+ else if (applications.TryGetValue(applicationId, out ContentMetaData content))
+ {
+ mainNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Program, device.Configuration.UserChannelPersistence.Index);
+ controlNca = content.GetNcaByType(device.FileSystem.KeySet, ContentType.Control, device.Configuration.UserChannelPersistence.Index);
+ }
ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
}
@@ -79,54 +102,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return (false, ProcessResult.Failed);
}
- // Load Update NCAs.
- Nca updatePatchNca = null;
- Nca updateControlNca = null;
-
- if (ulong.TryParse(mainNca.Header.TitleId.ToString("x16"), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase))
- {
- // Clear the program index part.
- titleIdBase &= ~0xFUL;
-
- // Load update information if exists.
- string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
- if (File.Exists(titleUpdateMetadataPath))
- {
- string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
- if (File.Exists(updatePath))
- {
- PartitionFileSystem updatePartitionFileSystem = new();
- updatePartitionFileSystem.Initialize(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()).ThrowIfFailure();
-
- device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem);
-
- // TODO: This should use CNMT NCA instead.
- foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca"))
- {
- Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath);
-
- if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
- {
- continue;
- }
-
- if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16"))
- {
- break;
- }
-
- if (nca.IsProgram())
- {
- updatePatchNca = nca;
- }
- else if (nca.IsControl())
- {
- updateControlNca = nca;
- }
- }
- }
- }
- }
+ (Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
if (updatePatchNca != null)
{
@@ -138,10 +114,8 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
controlNca = updateControlNca;
}
- // Load contained DownloadableContents.
// TODO: If we want to support multi-processes in future, we shouldn't clear AddOnContent data here.
device.Configuration.ContentManager.ClearAocData();
- device.Configuration.ContentManager.AddAocData(partitionFileSystem, path, mainNca.Header.TitleId, device.Configuration.FsIntegrityCheckLevel);
// Load DownloadableContents.
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
@@ -153,9 +127,12 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
{
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
{
- if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled)
+ if (File.Exists(downloadableContentContainer.ContainerPath))
{
- device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
+ if (downloadableContentNca.Enabled)
+ {
+ device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
+ }
}
else
{
@@ -168,18 +145,18 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return (true, mainNca.Load(device, patchNca, controlNca));
}
- errorMessage = "Unable to load: Could not find Main NCA";
+ errorMessage = $"Unable to load: Could not find Main NCA for title \"{applicationId:X16}\"";
return (false, ProcessResult.Failed);
}
- public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path)
+ public static Nca GetNca(this IFileSystem fileSystem, KeySet keySet, string path)
{
using var ncaFile = new UniqueRef<IFile>();
fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
- return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage());
+ return new Nca(keySet, ncaFile.Release().AsStorage());
}
}
}
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
index e5056c89..12d9c8bd 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.Loaders.Processes
_processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
}
- public bool LoadXci(string path)
+ public bool LoadXci(string path, ulong applicationId)
{
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
@@ -44,7 +44,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return false;
}
- (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage);
+ (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
if (!success)
{
@@ -66,13 +66,13 @@ namespace Ryujinx.HLE.Loaders.Processes
return false;
}
- public bool LoadNsp(string path)
+ public bool LoadNsp(string path, ulong applicationId)
{
FileStream file = new(path, FileMode.Open, FileAccess.Read);
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
- (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage);
+ (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
if (processResult.ProcessId == 0)
{
diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
index fe2aaac6..cf4eb416 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs
@@ -43,15 +43,14 @@ namespace Ryujinx.HLE.Loaders.Processes
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
{
- Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
+ Nca nca = partitionFileSystem.GetNca(device.FileSystem.KeySet, fileEntry.FullPath);
- if (!nca.IsProgram() && nca.IsPatch())
+ if (!nca.IsProgram())
{
continue;
}
- ulong currentProgramId = nca.Header.TitleId;
- ulong currentMainProgramId = currentProgramId & ~0xFFFul;
+ ulong currentMainProgramId = nca.GetProgramIdBase();
if (applicationId == 0 && currentMainProgramId != 0)
{
@@ -68,7 +67,7 @@ namespace Ryujinx.HLE.Loaders.Processes
break;
}
- hasIndex[(int)(currentProgramId & 0xF)] = true;
+ hasIndex[nca.GetProgramIndex()] = true;
}
if (programCount == 0)
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index 81c3ab47..9dfc6989 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -73,9 +73,9 @@ namespace Ryujinx.HLE
return Processes.LoadUnpackedNca(exeFsDir, romFsFile);
}
- public bool LoadXci(string xciFile)
+ public bool LoadXci(string xciFile, ulong applicationId = 0)
{
- return Processes.LoadXci(xciFile);
+ return Processes.LoadXci(xciFile, applicationId);
}
public bool LoadNca(string ncaFile)
@@ -83,9 +83,9 @@ namespace Ryujinx.HLE
return Processes.LoadNca(ncaFile);
}
- public bool LoadNsp(string nspFile)
+ public bool LoadNsp(string nspFile, ulong applicationId = 0)
{
- return Processes.LoadNsp(nspFile);
+ return Processes.LoadNsp(nspFile, applicationId);
}
public bool LoadProgram(string fileName)
diff --git a/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs b/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs
new file mode 100644
index 00000000..3c4ce085
--- /dev/null
+++ b/src/Ryujinx.HLE/Utilities/PartitionFileSystemUtils.cs
@@ -0,0 +1,45 @@
+using LibHac;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.Tools.Fs;
+using LibHac.Tools.FsSystem;
+using Ryujinx.HLE.FileSystem;
+using System.IO;
+
+namespace Ryujinx.HLE.Utilities
+{
+ public static class PartitionFileSystemUtils
+ {
+ public static IFileSystem OpenApplicationFileSystem(string path, VirtualFileSystem fileSystem, bool throwOnFailure = true)
+ {
+ FileStream file = File.OpenRead(path);
+
+ IFileSystem partitionFileSystem;
+
+ if (Path.GetExtension(path).ToLower() == ".xci")
+ {
+ partitionFileSystem = new Xci(fileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
+ }
+ else
+ {
+ var pfsTemp = new PartitionFileSystem();
+ Result initResult = pfsTemp.Initialize(file.AsStorage());
+
+ if (throwOnFailure)
+ {
+ initResult.ThrowIfFailure();
+ }
+ else if (initResult.IsFailure())
+ {
+ return null;
+ }
+
+ partitionFileSystem = pfsTemp;
+ }
+
+ fileSystem.ImportTickets(partitionFileSystem);
+
+ return partitionFileSystem;
+ }
+ }
+}