forked from MeloNX/MeloNX
extend stream loading support
(cherry picked from commit cff4a63e5a6deb33734f296b550723f0c6a9a693)
This commit is contained in:
parent
6919b123b7
commit
7de794cf81
@ -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<ulong, List<(NcaContentType type, string path)>> 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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> 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<ulong, List<(NcaContentType type, string path)>> 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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<ulong, List<(NcaContentType type, string path)>> 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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<IFile>();
|
||||
|
||||
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<byte> content = new(contentData);
|
||||
|
||||
contentStorage.Read(0, content);
|
||||
|
||||
Span<byte> 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()
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||
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 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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user