aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
diff options
context:
space:
mode:
authorAc_K <Acoustik666@gmail.com>2023-03-31 21:16:46 +0200
committerGitHub <noreply@github.com>2023-03-31 21:16:46 +0200
commit4c2d9ff3ff9d7afb1fd0bd764bee5931fa5f053c (patch)
tree1245f5ec356551bd20a9594d114d06a53c41d036 /Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
parent8198b99935f562ffb2fb9a75175a8df24d235152 (diff)
HLE: Refactoring of ApplicationLoader (#4480)1.1.689
* HLE: Refactoring of ApplicationLoader * Fix SDL2 Headless * Addresses gdkchan feedback * Fixes LoadUnpackedNca RomFS loading * remove useless casting * Cleanup and fixe empty application name * Remove ProcessInfo * Fixes typo * ActiveProcess to ActiveApplication * Update check * Clean using. * Use the correct filepath when loading Homebrew.npdm * Fix NRE in ProcessResult if MetaLoader is null * Add more checks for valid processId & return success * Add missing logging statement for npdm error * Return result for LoadKip() * Move error logging out of PFS load extension method This avoids logging "Could not find Main NCA" followed by "Loading main..." when trying to start hbl. * Fix GUIs not checking load results * Fix style and formatting issues * Fix formatting and wording * gtk: Refactor LoadApplication() --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Diffstat (limited to 'Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs')
-rw-r--r--Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs177
1 files changed, 177 insertions, 0 deletions
diff --git a/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
new file mode 100644
index 00000000..5147f5c3
--- /dev/null
+++ b/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs
@@ -0,0 +1,177 @@
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.Tools.Fs;
+using LibHac.Tools.FsSystem;
+using LibHac.Tools.FsSystem.NcaUtils;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+
+namespace Ryujinx.HLE.Loaders.Processes.Extensions
+{
+ public static class PartitionFileSystemExtensions
+ {
+ internal static (bool, ProcessResult) TryLoad(this PartitionFileSystem partitionFileSystem, Switch device, string path, out string errorMessage)
+ {
+ errorMessage = null;
+
+ // Load required NCAs.
+ Nca mainNca = null;
+ Nca patchNca = null;
+ Nca controlNca = null;
+
+ try
+ {
+ device.Configuration.VirtualFileSystem.ImportTickets(partitionFileSystem);
+
+ // TODO: To support multi-games container, this should use CNMT NCA instead.
+ foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
+ {
+ Nca nca = partitionFileSystem.GetNca(device, fileEntry.FullPath);
+
+ if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
+ {
+ continue;
+ }
+
+ if (nca.IsPatch())
+ {
+ patchNca = nca;
+ }
+ else if (nca.IsProgram())
+ {
+ mainNca = nca;
+ }
+ else if (nca.IsControl())
+ {
+ controlNca = nca;
+ }
+ }
+
+ ProcessLoaderHelper.RegisterProgramMapInfo(device, partitionFileSystem).ThrowIfFailure();
+ }
+ catch (Exception ex)
+ {
+ errorMessage = $"Unable to load: {ex.Message}";
+
+ return (false, ProcessResult.Failed);
+ }
+
+ if (mainNca != null)
+ {
+ if (mainNca.Header.ContentType != NcaContentType.Program)
+ {
+ errorMessage = "Selected NCA file is not a \"Program\" NCA";
+
+ 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<TitleUpdateMetadata>(titleUpdateMetadataPath).Selected;
+ if (File.Exists(updatePath))
+ {
+ PartitionFileSystem updatePartitionFileSystem = new(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage());
+
+ 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)
+ {
+ patchNca = updatePatchNca;
+ }
+
+ if (updateControlNca != null)
+ {
+ 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");
+ if (File.Exists(addOnContentMetadataPath))
+ {
+ List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(addOnContentMetadataPath);
+
+ foreach (DownloadableContentContainer downloadableContentContainer in dlcContainerList)
+ {
+ foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
+ {
+ if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled)
+ {
+ device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
+ }
+ else
+ {
+ Logger.Warning?.Print(LogClass.Application, $"Cannot find AddOnContent file {downloadableContentContainer.ContainerPath}. It may have been moved or renamed.");
+ }
+ }
+ }
+ }
+
+ return (true, mainNca.Load(device, patchNca, controlNca));
+ }
+
+ errorMessage = "Unable to load: Could not find Main NCA";
+
+ return (false, ProcessResult.Failed);
+ }
+
+ 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(device.Configuration.VirtualFileSystem.KeySet, ncaFile.Release().AsStorage());
+ }
+ }
+} \ No newline at end of file