diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs index f0e224472..d35096ef8 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -483,6 +483,27 @@ namespace Ryujinx.HLE.FileSystem FinishInstallation(temporaryDirectory, registeredDirectory); } + public void InstallFirmware(Stream stream, bool isXci) + { + ContentPath.TryGetContentPath(StorageId.BuiltInSystem, out var contentPathString); + ContentPath.TryGetRealPath(contentPathString, out var contentDirectory); + string registeredDirectory = Path.Combine(contentDirectory, "registered"); + string temporaryDirectory = Path.Combine(contentDirectory, "temp"); + + if (!isXci) + { + using ZipArchive archive = new ZipArchive(stream); + InstallFromZip(archive, temporaryDirectory); + } + else + { + Xci xci = new(_virtualFileSystem.KeySet, stream.AsStorage()); + InstallFromCart(xci, temporaryDirectory); + } + + FinishInstallation(temporaryDirectory, registeredDirectory); + } + private void FinishInstallation(string temporaryDirectory, string registeredDirectory) { if (Directory.Exists(registeredDirectory)) @@ -601,13 +622,16 @@ namespace Ryujinx.HLE.FileSystem throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers."); } - Dictionary> updateNcas = new(); - if (Directory.Exists(firmwarePackage)) { return VerifyAndGetVersionDirectory(firmwarePackage); } + SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory) + { + return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory)); + } + if (!File.Exists(firmwarePackage)) { throw new FileNotFoundException("Firmware file does not exist."); @@ -615,249 +639,99 @@ namespace Ryujinx.HLE.FileSystem FileInfo info = new(firmwarePackage); - using FileStream file = File.OpenRead(firmwarePackage); - switch (info.Extension) + if (info.Extension == ".zip" || info.Extension == ".xci") { - case ".zip": - using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage)) - { - return VerifyAndGetVersionZip(archive); - } - case ".xci": - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); + using FileStream file = File.OpenRead(firmwarePackage); - if (xci.HasPartition(XciPartitionType.Update)) - { - XciPartition partition = xci.OpenPartition(XciPartitionType.Update); + var isXci = info.Extension == ".xci"; - return VerifyAndGetVersion(partition); - } - else - { - throw new InvalidFirmwarePackageException("Update not found in xci file."); - } - default: - break; + return VerifyFirmwarePackage(file, isXci); } - SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory) + return null; + } + + public SystemVersion VerifyFirmwarePackage(Stream file, bool isXci) + { + if (!isXci) { - return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory)); + using ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Read); + return VerifyAndGetVersionZip(archive); } - - SystemVersion VerifyAndGetVersionZip(ZipArchive archive) + else { - SystemVersion systemVersion = null; + Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - foreach (var entry in archive.Entries) + if (xci.HasPartition(XciPartitionType.Update)) { - if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00")) - { - using Stream ncaStream = GetZipStream(entry); - IStorage storage = ncaStream.AsStorage(); + XciPartition partition = xci.OpenPartition(XciPartitionType.Update); - Nca nca = new(_virtualFileSystem.KeySet, storage); - - if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) - { - updateNcasItem.Add((nca.Header.ContentType, entry.FullName)); - } - else - { - updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>()); - updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName)); - } - } - } - - if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry)) - { - string metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; - - CnmtContentMetaEntry[] metaEntries = null; - - var fileEntry = archive.GetEntry(metaPath); - - using (Stream ncaStream = GetZipStream(fileEntry)) - { - Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; - - using var metaFile = new UniqueRef(); - - if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) - { - var meta = new Cnmt(metaFile.Get.AsStream()); - - if (meta.Type == ContentMetaType.SystemUpdate) - { - metaEntries = meta.MetaEntries; - - updateNcas.Remove(SystemUpdateTitleId); - } - } - } - - if (metaEntries == null) - { - throw new FileNotFoundException("System update title was not found in the firmware package."); - } - - if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem)) - { - string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path; - - using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)); - Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); - - var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - using var systemVersionFile = new UniqueRef(); - - if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) - { - systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); - } - } - - foreach (CnmtContentMetaEntry metaEntry in metaEntries) - { - if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry)) - { - metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; - - string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; - - // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. - // This is a perfect valid case, so we should just ignore the missing content nca and continue. - if (contentPath == null) - { - updateNcas.Remove(metaEntry.TitleId); - - continue; - } - - ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath); - ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath); - - using Stream metaNcaStream = GetZipStream(metaZipEntry); - using Stream contentNcaStream = GetZipStream(contentZipEntry); - Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); - - IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; - - using var metaFile = new UniqueRef(); - - if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) - { - var meta = new Cnmt(metaFile.Get.AsStream()); - - IStorage contentStorage = contentNcaStream.AsStorage(); - if (contentStorage.GetSize(out long size).IsSuccess()) - { - byte[] contentData = new byte[size]; - - Span content = new(contentData); - - contentStorage.Read(0, content); - - Span hash = new(new byte[32]); - - LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash); - - if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash)) - { - updateNcas.Remove(metaEntry.TitleId); - } - } - } - } - } - - if (updateNcas.Count > 0) - { - StringBuilder extraNcas = new(); - - foreach (var entry in updateNcas) - { - foreach (var (type, path) in entry.Value) - { - extraNcas.AppendLine(path); - } - } - - throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}"); - } + return VerifyAndGetVersion(partition); } else { - throw new FileNotFoundException("System update title was not found in the firmware package."); + throw new InvalidFirmwarePackageException("Update not found in xci file."); } - - return systemVersion; } + } - SystemVersion VerifyAndGetVersion(IFileSystem filesystem) + private SystemVersion VerifyAndGetVersionZip(ZipArchive archive) + { + Dictionary> updateNcas = new(); + + SystemVersion systemVersion = null; + + foreach (var entry in archive.Entries) { - SystemVersion systemVersion = null; - - CnmtContentMetaEntry[] metaEntries = null; - - foreach (var entry in filesystem.EnumerateEntries("/", "*.nca")) + if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00")) { - IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage(); + using Stream ncaStream = GetZipStream(entry); + IStorage storage = ncaStream.AsStorage(); - Nca nca = new(_virtualFileSystem.KeySet, ncaStorage); - - if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta) - { - IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; - - using var metaFile = new UniqueRef(); - - if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) - { - var meta = new Cnmt(metaFile.Get.AsStream()); - - if (meta.Type == ContentMetaType.SystemUpdate) - { - metaEntries = meta.MetaEntries; - } - } - - continue; - } - else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) - { - var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - using var systemVersionFile = new UniqueRef(); - - if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) - { - systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); - } - } + Nca nca = new(_virtualFileSystem.KeySet, storage); if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) { - updateNcasItem.Add((nca.Header.ContentType, entry.FullPath)); + updateNcasItem.Add((nca.Header.ContentType, entry.FullName)); } else { updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>()); - updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath)); + updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName)); } + } + } - ncaStorage.Dispose(); + if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry)) + { + string metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; + + CnmtContentMetaEntry[] metaEntries = null; + + var fileEntry = archive.GetEntry(metaPath); + + using (Stream ncaStream = GetZipStream(fileEntry)) + { + Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); + + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; + + using var metaFile = new UniqueRef(); + + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + { + var meta = new Cnmt(metaFile.Get.AsStream()); + + if (meta.Type == ContentMetaType.SystemUpdate) + { + metaEntries = meta.MetaEntries; + + updateNcas.Remove(SystemUpdateTitleId); + } + } } if (metaEntries == null) @@ -865,11 +739,29 @@ namespace Ryujinx.HLE.FileSystem throw new FileNotFoundException("System update title was not found in the firmware package."); } + if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem)) + { + string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path; + + using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)); + Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); + + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + using var systemVersionFile = new UniqueRef(); + + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) + { + systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); + } + } + foreach (CnmtContentMetaEntry metaEntry in metaEntries) { - if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry)) + if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry)) { - string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; + metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; + string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. @@ -881,10 +773,12 @@ namespace Ryujinx.HLE.FileSystem continue; } - IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage(); - IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage(); + ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath); + ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath); - Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage); + using Stream metaNcaStream = GetZipStream(metaZipEntry); + using Stream contentNcaStream = GetZipStream(contentZipEntry); + Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage()); IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); @@ -896,6 +790,7 @@ namespace Ryujinx.HLE.FileSystem { var meta = new Cnmt(metaFile.Get.AsStream()); + IStorage contentStorage = contentNcaStream.AsStorage(); if (contentStorage.GetSize(out long size).IsSuccess()) { byte[] contentData = new byte[size]; @@ -931,11 +826,147 @@ namespace Ryujinx.HLE.FileSystem throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}"); } - - return systemVersion; + } + else + { + throw new FileNotFoundException("System update title was not found in the firmware package."); } - return null; + return systemVersion; + } + + private SystemVersion VerifyAndGetVersion(IFileSystem filesystem) + { + Dictionary> updateNcas = new(); + + SystemVersion systemVersion = null; + + CnmtContentMetaEntry[] metaEntries = null; + + foreach (var entry in filesystem.EnumerateEntries("/", "*.nca")) + { + IStorage ncaStorage = OpenPossibleFragmentedFile(filesystem, entry.FullPath, OpenMode.Read).AsStorage(); + + Nca nca = new(_virtualFileSystem.KeySet, ncaStorage); + + if (nca.Header.TitleId == SystemUpdateTitleId && nca.Header.ContentType == NcaContentType.Meta) + { + IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; + + using var metaFile = new UniqueRef(); + + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + { + var meta = new Cnmt(metaFile.Get.AsStream()); + + if (meta.Type == ContentMetaType.SystemUpdate) + { + metaEntries = meta.MetaEntries; + } + } + + continue; + } + else if (nca.Header.TitleId == SystemVersionTitleId && nca.Header.ContentType == NcaContentType.Data) + { + var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + using var systemVersionFile = new UniqueRef(); + + if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess()) + { + systemVersion = new SystemVersion(systemVersionFile.Get.AsStream()); + } + } + + if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem)) + { + updateNcasItem.Add((nca.Header.ContentType, entry.FullPath)); + } + else + { + updateNcas.Add(nca.Header.TitleId, new List<(NcaContentType, string)>()); + updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullPath)); + } + + ncaStorage.Dispose(); + } + + if (metaEntries == null) + { + throw new FileNotFoundException("System update title was not found in the firmware package."); + } + + foreach (CnmtContentMetaEntry metaEntry in metaEntries) + { + if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry)) + { + string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; + string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; + + // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. + // This is a perfect valid case, so we should just ignore the missing content nca and continue. + if (contentPath == null) + { + updateNcas.Remove(metaEntry.TitleId); + + continue; + } + + IStorage metaStorage = OpenPossibleFragmentedFile(filesystem, metaNcaPath, OpenMode.Read).AsStorage(); + IStorage contentStorage = OpenPossibleFragmentedFile(filesystem, contentPath, OpenMode.Read).AsStorage(); + + Nca metaNca = new(_virtualFileSystem.KeySet, metaStorage); + + IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); + + string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; + + using var metaFile = new UniqueRef(); + + if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess()) + { + var meta = new Cnmt(metaFile.Get.AsStream()); + + if (contentStorage.GetSize(out long size).IsSuccess()) + { + byte[] contentData = new byte[size]; + + Span content = new(contentData); + + contentStorage.Read(0, content); + + Span hash = new(new byte[32]); + + LibHac.Crypto.Sha256.GenerateSha256Hash(content, hash); + + if (LibHac.Common.Utilities.ArraysEqual(hash.ToArray(), meta.ContentEntries[0].Hash)) + { + updateNcas.Remove(metaEntry.TitleId); + } + } + } + } + } + + if (updateNcas.Count > 0) + { + StringBuilder extraNcas = new(); + + foreach (var entry in updateNcas) + { + foreach (var (type, path) in entry.Value) + { + extraNcas.AppendLine(path); + } + } + + throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}"); + } + + return systemVersion; } public SystemVersion GetCurrentFirmwareVersion() diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 2928ac7fe..5fee06f21 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -127,7 +127,7 @@ 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) + public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath, Stream updateStream = null) { updatePath = null; @@ -138,28 +138,37 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions // Clear the program index part. ulong titleIdBase = mainNca.GetProgramIdBase(); - // Load update information if exists. - string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); - if (File.Exists(titleUpdateMetadataPath)) + IFileSystem updatePartitionFileSystem = null; + + if (updateStream == null) { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected; - if (File.Exists(updatePath)) + // Load update information if exists. + string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); + if (File.Exists(titleUpdateMetadataPath)) { - IFileSystem updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem); - - foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel)) + updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected; + if (File.Exists(updatePath)) { - if ((applicationTitleId & ~0x1FFFUL) != titleIdBase) - { - continue; - } - - updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex); - updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex); - break; + updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem); } } } + else + { + updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updateStream, false, 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); } diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs index a6dde3a68..e8feb719e 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/PartitionFileSystemExtensions.cs @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions return programs; } - internal static (bool, ProcessResult) TryLoad(this PartitionFileSystemCore partitionFileSystem, Switch device, Stream stream, ulong applicationId, out string errorMessage, string extension) + internal static (bool, ProcessResult) TryLoad(this PartitionFileSystemCore partitionFileSystem, Switch device, Stream stream, ulong applicationId, out string errorMessage, string extension, Stream updateStream = null) where TMetaData : PartitionFileSystemMetaCore, new() where TFormat : IPartitionFileSystemFormat where THeader : unmanaged, IPartitionFileSystemHeader @@ -102,7 +102,7 @@ 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 _); + (Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _, updateStream); if (updatePatchNca != null) { diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index cef1667e1..acf59ec88 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.Loaders.Processes return LoadXci(stream, applicationId); } - public bool LoadXci(Stream stream, ulong applicationId) + public bool LoadXci(Stream stream, ulong applicationId, Stream updateStream = null) { Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage()); @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, applicationId, out string errorMessage, "xci"); + (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, applicationId, out string errorMessage, "xci", updateStream); if (!success) { @@ -79,12 +79,12 @@ namespace Ryujinx.HLE.Loaders.Processes return LoadNsp(file, applicationId); } - public bool LoadNsp(Stream stream, ulong applicationId) + public bool LoadNsp(Stream stream, ulong applicationId, Stream updateStream = null) { PartitionFileSystem partitionFileSystem = new(); partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure(); - (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, applicationId, out string errorMessage, "nsp"); + (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, applicationId, out string errorMessage, "nsp", updateStream); if (processResult.ProcessId == 0) { diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 58fc7ac74..ad45e99e6 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -94,7 +94,7 @@ namespace Ryujinx.HLE return Processes.LoadNxo(fileName); } - public bool LoadXci(Stream xciStream) + public bool LoadXci(Stream xciStream, Stream updateStream = null) { return Processes.LoadXci(xciStream); } @@ -104,9 +104,9 @@ namespace Ryujinx.HLE return Processes.LoadNca(ncaStream); } - public bool LoadNsp(Stream nspStream) + public bool LoadNsp(Stream nspStream, Stream updateStream = null) { - return Processes.LoadNsp(nspStream); + return Processes.LoadNsp(nspStream, updateStream); } public bool LoadProgram(Stream stream, bool isNro, string name)