mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-07-22 14:47:10 +02:00
android - implement firmware installation
This commit is contained in:
parent
60f320bc07
commit
aa87b4abd8
9 changed files with 669 additions and 283 deletions
|
@ -525,6 +525,27 @@ namespace Ryujinx.HLE.FileSystem
|
|||
FinishInstallation(temporaryDirectory, registeredDirectory);
|
||||
}
|
||||
|
||||
public void InstallFirmware(Stream stream, bool isXci)
|
||||
{
|
||||
string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem);
|
||||
string contentDirectory = ContentPath.GetRealPath(contentPathString);
|
||||
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))
|
||||
|
@ -643,13 +664,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.");
|
||||
|
@ -657,249 +681,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)
|
||||
|
@ -907,11 +781,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.
|
||||
|
@ -923,10 +815,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);
|
||||
|
||||
|
@ -938,6 +832,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];
|
||||
|
@ -973,11 +868,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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue