1
0
forked from MeloNX/MeloNX

extend stream loading support

(cherry picked from commit cff4a63e5a6deb33734f296b550723f0c6a9a693)
This commit is contained in:
Emmanuel Hansen 2023-12-30 12:35:38 +00:00
parent 6919b123b7
commit 7de794cf81
5 changed files with 289 additions and 249 deletions

@ -483,6 +483,27 @@ namespace Ryujinx.HLE.FileSystem
FinishInstallation(temporaryDirectory, registeredDirectory); 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) private void FinishInstallation(string temporaryDirectory, string registeredDirectory)
{ {
if (Directory.Exists(registeredDirectory)) if (Directory.Exists(registeredDirectory))
@ -601,13 +622,16 @@ namespace Ryujinx.HLE.FileSystem
throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers."); throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers.");
} }
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
if (Directory.Exists(firmwarePackage)) if (Directory.Exists(firmwarePackage))
{ {
return VerifyAndGetVersionDirectory(firmwarePackage); return VerifyAndGetVersionDirectory(firmwarePackage);
} }
SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory)
{
return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory));
}
if (!File.Exists(firmwarePackage)) if (!File.Exists(firmwarePackage))
{ {
throw new FileNotFoundException("Firmware file does not exist."); throw new FileNotFoundException("Firmware file does not exist.");
@ -615,16 +639,28 @@ namespace Ryujinx.HLE.FileSystem
FileInfo info = new(firmwarePackage); FileInfo info = new(firmwarePackage);
if (info.Extension == ".zip" || info.Extension == ".xci")
{
using FileStream file = File.OpenRead(firmwarePackage); using FileStream file = File.OpenRead(firmwarePackage);
switch (info.Extension) var isXci = info.Extension == ".xci";
return VerifyFirmwarePackage(file, isXci);
}
return null;
}
public SystemVersion VerifyFirmwarePackage(Stream file, bool isXci)
{ {
case ".zip": if (!isXci)
using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage))
{ {
using ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Read);
return VerifyAndGetVersionZip(archive); return VerifyAndGetVersionZip(archive);
} }
case ".xci": else
{
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
if (xci.HasPartition(XciPartitionType.Update)) if (xci.HasPartition(XciPartitionType.Update))
@ -637,17 +673,13 @@ namespace Ryujinx.HLE.FileSystem
{ {
throw new InvalidFirmwarePackageException("Update not found in xci file."); throw new InvalidFirmwarePackageException("Update not found in xci file.");
} }
default: }
break;
} }
SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory) private SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
{ {
return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory)); Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
}
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
{
SystemVersion systemVersion = null; SystemVersion systemVersion = null;
foreach (var entry in archive.Entries) foreach (var entry in archive.Entries)
@ -803,8 +835,10 @@ namespace Ryujinx.HLE.FileSystem
return systemVersion; return systemVersion;
} }
SystemVersion VerifyAndGetVersion(IFileSystem filesystem) private SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
{ {
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
SystemVersion systemVersion = null; SystemVersion systemVersion = null;
CnmtContentMetaEntry[] metaEntries = null; CnmtContentMetaEntry[] metaEntries = null;
@ -935,9 +969,6 @@ namespace Ryujinx.HLE.FileSystem
return systemVersion; return systemVersion;
} }
return null;
}
public SystemVersion GetCurrentFirmwareVersion() public SystemVersion GetCurrentFirmwareVersion()
{ {
LoadEntries(); LoadEntries();

@ -127,7 +127,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return nca.Header.ContentType == NcaContentType.Control; 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; updatePath = null;
@ -138,6 +138,10 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
// Clear the program index part. // Clear the program index part.
ulong titleIdBase = mainNca.GetProgramIdBase(); ulong titleIdBase = mainNca.GetProgramIdBase();
IFileSystem updatePartitionFileSystem = null;
if (updateStream == null)
{
// Load update information if exists. // Load update information if exists.
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath)) if (File.Exists(titleUpdateMetadataPath))
@ -145,7 +149,14 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected; updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
if (File.Exists(updatePath)) if (File.Exists(updatePath))
{ {
IFileSystem updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem); updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem);
}
}
}
else
{
updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updateStream, false, fileSystem);
}
foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel)) foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel))
{ {
@ -158,8 +169,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex); updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
break; break;
} }
}
}
return (updatePatchNca, updateControlNca); return (updatePatchNca, updateControlNca);
} }

@ -52,7 +52,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return programs; return programs;
} }
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, Stream stream, ulong applicationId, out string errorMessage, string extension) internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, Stream stream, ulong applicationId, out string errorMessage, string extension, Stream updateStream = null)
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new() where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
where TFormat : IPartitionFileSystemFormat where TFormat : IPartitionFileSystemFormat
where THeader : unmanaged, IPartitionFileSystemHeader where THeader : unmanaged, IPartitionFileSystemHeader
@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
return (false, ProcessResult.Failed); 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) if (updatePatchNca != null)
{ {

@ -39,7 +39,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return LoadXci(stream, applicationId); 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()); Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
@ -50,7 +50,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return false; 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) if (!success)
{ {
@ -79,12 +79,12 @@ namespace Ryujinx.HLE.Loaders.Processes
return LoadNsp(file, applicationId); 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 partitionFileSystem = new();
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure(); 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) if (processResult.ProcessId == 0)
{ {

@ -94,7 +94,7 @@ namespace Ryujinx.HLE
return Processes.LoadNxo(fileName); return Processes.LoadNxo(fileName);
} }
public bool LoadXci(Stream xciStream) public bool LoadXci(Stream xciStream, Stream updateStream = null)
{ {
return Processes.LoadXci(xciStream); return Processes.LoadXci(xciStream);
} }
@ -104,9 +104,9 @@ namespace Ryujinx.HLE
return Processes.LoadNca(ncaStream); 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) public bool LoadProgram(Stream stream, bool isNro, string name)