aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/Loaders/Processes/Extensions
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/Loaders/Processes/Extensions')
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs100
-rw-r--r--src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs153
2 files changed, 76 insertions, 177 deletions
diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
index 6863d1a7..4568b44d 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs
@@ -2,31 +2,21 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
-using LibHac.FsSystem;
using LibHac.Loader;
using LibHac.Ncm;
using LibHac.Ns;
-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 Ryujinx.HLE.HOS;
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
{
- public static class NcaExtensions
+ static class NcaExtensions
{
- private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
-
public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca)
{
// Extract RomFs and ExeFs from NCA.
@@ -57,7 +47,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" non-existent update.
+ /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" inexistant 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 _);
@@ -96,11 +86,6 @@ 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);
@@ -111,11 +96,6 @@ 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);
@@ -128,56 +108,6 @@ 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 = "(unknown)";
-
- // 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, _titleSerializerContext.TitleUpdateMetadata).Selected;
- if (File.Exists(updatePath))
- {
- var updateFile = new FileStream(updatePath, FileMode.Open, FileAccess.Read);
-
- IFileSystem updatePartitionFileSystem;
-
- if (Path.GetExtension(updatePath).ToLower() == ".xci")
- {
- updatePartitionFileSystem = new Xci(fileSystem.KeySet, updateFile.AsStorage()).OpenPartition(XciPartitionType.Secure);
- }
- else
- {
- PartitionFileSystem pfsTemp = new();
- pfsTemp.Initialize(updateFile.AsStorage()).ThrowIfFailure();
- updatePartitionFileSystem = pfsTemp;
- }
-
- foreach ((ulong updateTitleId, ContentCollection content) in updatePartitionFileSystem.GetUpdateData(fileSystem, checkLevel))
- {
- if ((updateTitleId & ~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;
@@ -242,31 +172,5 @@ 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 5f45cd45..50f7d585 100644
--- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
+++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
@@ -1,87 +1,26 @@
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());
- public static Dictionary<ulong, ContentCollection> GetApplicationData(this IFileSystem partitionFileSystem,
- VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
- {
- fileSystem.ImportTickets(partitionFileSystem);
-
- var programs = new Dictionary<ulong, ContentCollection>();
-
- foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
- {
- Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Application);
-
- if (cnmt == null)
- {
- continue;
- }
-
- ContentCollection content = new(partitionFileSystem, cnmt);
-
- if (content.Type != ContentMetaType.Application)
- {
- continue;
- }
-
- programs.TryAdd(content.ApplicationId, content);
- }
-
- return programs;
- }
-
- public static Dictionary<ulong, ContentCollection> GetUpdateData(this IFileSystem partitionFileSystem,
- VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel)
- {
- fileSystem.ImportTickets(partitionFileSystem);
-
- var programs = new Dictionary<ulong, ContentCollection>();
-
- foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.cnmt.nca"))
- {
- Cnmt cnmt = partitionFileSystem.GetNca(fileSystem.KeySet, fileEntry.FullPath).GetCnmt(checkLevel, ContentMetaType.Patch);
-
- if (cnmt == null)
- {
- continue;
- }
-
- ContentCollection content = new(partitionFileSystem, cnmt);
-
- if (content.Type != ContentMetaType.Patch)
- {
- 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 titleId, out string errorMessage)
+ internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, out string errorMessage)
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
where TFormat : IPartitionFileSystemFormat
where THeader : unmanaged, IPartitionFileSystemHeader
@@ -96,21 +35,30 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
try
{
- Dictionary<ulong, ContentCollection> applications = partitionFileSystem.GetApplicationData(device.FileSystem, device.System.FsIntegrityCheckLevel);
+ device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem);
- if (titleId == 0)
+ // TODO: To support multi-games container, this should use CNMT NCA instead.
+ foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
{
- foreach ((ulong _, ContentCollection content) in applications)
+ Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
+
+ if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
{
- 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;
+ continue;
+ }
+
+ if (nca.IsPatch())
+ {
+ patchNca = nca;
+ }
+ else if (nca.IsProgram())
+ {
+ mainNca = nca;
+ }
+ else if (nca.IsControl())
+ {
+ controlNca = nca;
}
- }
- else if (applications.TryGetValue(titleId, out ContentCollection 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();
@@ -131,7 +79,54 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return (false, ProcessResult.Failed);
}
- (Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
+ // 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;
+ }
+ }
+ }
+ }
+ }
if (updatePatchNca != null)
{
@@ -173,18 +168,18 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return (true, mainNca.Load(device, patchNca, controlNca));
}
- errorMessage = $"Unable to load: Could not find Main NCA for title \"{titleId:X16}\"";
+ errorMessage = "Unable to load: Could not find Main NCA";
return (false, ProcessResult.Failed);
}
- public static Nca GetNca(this IFileSystem fileSystem, KeySet keySet, string path)
+ public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path)
{
using var ncaFile = new UniqueRef<IFile>();
fileSystem.OpenFile(ref ncaFile.Ref, path.ToU8Span(), OpenMode.Read).ThrowIfFailure();
- return new Nca(keySet, ncaFile.Release().AsStorage());
+ return new Nca(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage());
}
}
}