mirror of
https://git.ryujinx.app/kenji-nx/ryujinx.git
synced 2025-12-13 13:37:08 +00:00
Revert stream loader changes
"add stream based loaders" - Commitc16559f2"Fix some DLCs crashing due to stream loader change" - Commita62deb8c
This commit is contained in:
parent
fe7a30c747
commit
a1834900be
6 changed files with 284 additions and 487 deletions
|
|
@ -43,15 +43,11 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
private readonly struct AocItem
|
private readonly struct AocItem
|
||||||
{
|
{
|
||||||
public readonly string ContainerPath;
|
public readonly string ContainerPath;
|
||||||
public readonly Stream ContainerStream;
|
|
||||||
public readonly string NcaPath;
|
public readonly string NcaPath;
|
||||||
public readonly string Extension;
|
|
||||||
|
|
||||||
public AocItem(string containerPath, Stream containerStream, string ncaPath, string extension)
|
public AocItem(string containerPath, string ncaPath)
|
||||||
{
|
{
|
||||||
ContainerPath = containerPath;
|
ContainerPath = containerPath;
|
||||||
ContainerStream = containerStream;
|
|
||||||
Extension = extension;
|
|
||||||
NcaPath = ncaPath;
|
NcaPath = ncaPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,10 +187,10 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAocItem(ulong titleId, string containerPath, Stream containerStream, string ncaPath, string extension, bool mergedToContainer = false)
|
public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false)
|
||||||
{
|
{
|
||||||
// TODO: Check Aoc version.
|
// TODO: Check Aoc version.
|
||||||
if (!AocData.TryAdd(titleId, new AocItem(containerPath, containerStream, ncaPath, extension)))
|
if (!AocData.TryAdd(titleId, new AocItem(containerPath, ncaPath)))
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
|
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
|
||||||
}
|
}
|
||||||
|
|
@ -204,20 +200,12 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
if (!mergedToContainer)
|
if (!mergedToContainer)
|
||||||
{
|
{
|
||||||
using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerStream, extension == ".xci", _virtualFileSystem);
|
using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerPath, _virtualFileSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearAocData()
|
public void ClearAocData() => AocData.Clear();
|
||||||
{
|
|
||||||
foreach (var aoc in AocData)
|
|
||||||
{
|
|
||||||
aoc.Value.ContainerStream?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
AocData.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetAocCount() => AocData.Count;
|
public int GetAocCount() => AocData.Count;
|
||||||
|
|
||||||
|
|
@ -232,11 +220,11 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read);
|
var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read);
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
switch (aoc.Extension)
|
switch (Path.GetExtension(aoc.ContainerPath))
|
||||||
{
|
{
|
||||||
case ".xci":
|
case ".xci":
|
||||||
var xci = new Xci(_virtualFileSystem.KeySet, aoc.ContainerStream.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
var xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||||
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read);
|
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
break;
|
break;
|
||||||
case ".nsp":
|
case ".nsp":
|
||||||
var pfs = new PartitionFileSystem();
|
var pfs = new PartitionFileSystem();
|
||||||
|
|
@ -488,27 +476,6 @@ 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InstallKeys(string keysSource, string installDirectory)
|
public void InstallKeys(string keysSource, string installDirectory)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(keysSource))
|
if (Directory.Exists(keysSource))
|
||||||
|
|
@ -695,16 +662,13 @@ 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.");
|
||||||
|
|
@ -712,144 +676,274 @@ 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);
|
||||||
|
|
||||||
|
switch (info.Extension)
|
||||||
{
|
{
|
||||||
using FileStream file = File.OpenRead(firmwarePackage);
|
case ".zip":
|
||||||
|
using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage))
|
||||||
var isXci = info.Extension == ".xci";
|
|
||||||
|
|
||||||
return VerifyFirmwarePackage(file, isXci);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SystemVersion VerifyFirmwarePackage(Stream file, bool isXci)
|
|
||||||
{
|
|
||||||
if (!isXci)
|
|
||||||
{
|
|
||||||
using ZipArchive archive = new ZipArchive(file, ZipArchiveMode.Read);
|
|
||||||
return VerifyAndGetVersionZip(archive);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
|
|
||||||
|
|
||||||
if (xci.HasPartition(XciPartitionType.Update))
|
|
||||||
{
|
|
||||||
XciPartition partition = xci.OpenPartition(XciPartitionType.Update);
|
|
||||||
|
|
||||||
return VerifyAndGetVersion(partition);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidFirmwarePackageException("Update not found in xci file.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
|
||||||
{
|
|
||||||
Dictionary<ulong, List<(NcaContentType type, string path)>> updateNcas = new();
|
|
||||||
|
|
||||||
SystemVersion systemVersion = null;
|
|
||||||
|
|
||||||
foreach (var entry in archive.Entries)
|
|
||||||
{
|
|
||||||
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
|
|
||||||
{
|
|
||||||
using Stream ncaStream = GetZipStream(entry);
|
|
||||||
IStorage storage = ncaStream.AsStorage();
|
|
||||||
|
|
||||||
Nca nca = new(_virtualFileSystem.KeySet, storage);
|
|
||||||
|
|
||||||
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
|
||||||
{
|
{
|
||||||
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
|
return VerifyAndGetVersionZip(archive);
|
||||||
}
|
}
|
||||||
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
case ".xci":
|
||||||
|
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
|
||||||
|
|
||||||
|
if (xci.HasPartition(XciPartitionType.Update))
|
||||||
{
|
{
|
||||||
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
|
XciPartition partition = xci.OpenPartition(XciPartitionType.Update);
|
||||||
|
|
||||||
|
return VerifyAndGetVersion(partition);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidFirmwarePackageException("Update not found in xci file.");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry))
|
SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory)
|
||||||
{
|
{
|
||||||
string metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
CnmtContentMetaEntry[] metaEntries = null;
|
SystemVersion VerifyAndGetVersionZip(ZipArchive archive)
|
||||||
|
{
|
||||||
|
SystemVersion systemVersion = null;
|
||||||
|
|
||||||
var fileEntry = archive.GetEntry(metaPath);
|
foreach (var entry in archive.Entries)
|
||||||
|
|
||||||
using (Stream ncaStream = GetZipStream(fileEntry))
|
|
||||||
{
|
{
|
||||||
Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
if (entry.FullName.EndsWith(".nca") || entry.FullName.EndsWith(".nca/00"))
|
||||||
|
|
||||||
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());
|
using Stream ncaStream = GetZipStream(entry);
|
||||||
|
IStorage storage = ncaStream.AsStorage();
|
||||||
|
|
||||||
if (meta.Type == ContentMetaType.SystemUpdate)
|
Nca nca = new(_virtualFileSystem.KeySet, storage);
|
||||||
|
|
||||||
|
if (updateNcas.TryGetValue(nca.Header.TitleId, out var updateNcasItem))
|
||||||
{
|
{
|
||||||
metaEntries = meta.MetaEntries;
|
updateNcasItem.Add((nca.Header.ContentType, entry.FullName));
|
||||||
|
}
|
||||||
updateNcas.Remove(SystemUpdateTitleId);
|
else if (updateNcas.TryAdd(nca.Header.TitleId, new List<(NcaContentType, string)>()))
|
||||||
|
{
|
||||||
|
updateNcas[nca.Header.TitleId].Add((nca.Header.ContentType, entry.FullName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metaEntries == null)
|
if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry))
|
||||||
{
|
{
|
||||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
string metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||||
}
|
|
||||||
|
|
||||||
if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem))
|
CnmtContentMetaEntry[] metaEntries = null;
|
||||||
{
|
|
||||||
string versionEntry = updateNcasItem.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
|
||||||
|
|
||||||
using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry));
|
var fileEntry = archive.GetEntry(metaPath);
|
||||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
|
||||||
|
|
||||||
var romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
using (Stream ncaStream = GetZipStream(fileEntry))
|
||||||
|
|
||||||
using var systemVersionFile = new UniqueRef<IFile>();
|
|
||||||
|
|
||||||
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
|
|
||||||
{
|
{
|
||||||
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
|
Nca metaNca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||||
}
|
|
||||||
}
|
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||||
|
|
||||||
foreach (CnmtContentMetaEntry metaEntry in metaEntries)
|
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
|
||||||
{
|
|
||||||
if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry))
|
using var metaFile = new UniqueRef<IFile>();
|
||||||
{
|
|
||||||
metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
|
||||||
|
{
|
||||||
string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
|
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||||
|
|
||||||
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
|
if (meta.Type == ContentMetaType.SystemUpdate)
|
||||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
{
|
||||||
if (contentPath == null)
|
metaEntries = meta.MetaEntries;
|
||||||
{
|
|
||||||
updateNcas.Remove(metaEntry.TitleId);
|
updateNcas.Remove(SystemUpdateTitleId);
|
||||||
|
}
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZipArchiveEntry metaZipEntry = archive.GetEntry(metaPath);
|
if (metaEntries == null)
|
||||||
ZipArchiveEntry contentZipEntry = archive.GetEntry(contentPath);
|
{
|
||||||
|
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||||
using Stream metaNcaStream = GetZipStream(metaZipEntry);
|
}
|
||||||
using Stream contentNcaStream = GetZipStream(contentZipEntry);
|
|
||||||
Nca metaNca = new(_virtualFileSystem.KeySet, metaNcaStream.AsStorage());
|
if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem))
|
||||||
|
{
|
||||||
|
string versionEntry = updateNcasItem.FirstOrDefault(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.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||||
|
|
||||||
|
string contentPath = ncaEntry.FirstOrDefault(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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return systemVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemVersion VerifyAndGetVersion(IFileSystem filesystem)
|
||||||
|
{
|
||||||
|
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 if (updateNcas.TryAdd(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.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
||||||
|
string contentPath = ncaEntry.FirstOrDefault(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);
|
IFileSystem fs = metaNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
|
||||||
|
|
||||||
|
|
@ -861,7 +955,6 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
var meta = new Cnmt(metaFile.Get.AsStream());
|
var meta = new Cnmt(metaFile.Get.AsStream());
|
||||||
|
|
||||||
IStorage contentStorage = contentNcaStream.AsStorage();
|
|
||||||
if (contentStorage.GetSize(out long size).IsSuccess())
|
if (contentStorage.GetSize(out long size).IsSuccess())
|
||||||
{
|
{
|
||||||
byte[] contentData = new byte[size];
|
byte[] contentData = new byte[size];
|
||||||
|
|
@ -897,146 +990,11 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
|
|
||||||
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
throw new InvalidFirmwarePackageException($"Firmware package contains unrelated archives. Please remove these paths: {Environment.NewLine}{extraNcas}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
return systemVersion;
|
||||||
{
|
|
||||||
throw new FileNotFoundException("System update title was not found in the firmware package.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return systemVersion;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
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 if (updateNcas.TryAdd(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.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
|
|
||||||
string contentPath = ncaEntry.FirstOrDefault(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()
|
public SystemVersion GetCurrentFirmwareVersion()
|
||||||
|
|
|
||||||
|
|
@ -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, Stream updateStream = null)
|
public static (Nca, Nca) GetUpdateData(this Nca mainNca, VirtualFileSystem fileSystem, IntegrityCheckLevel checkLevel, int programIndex, out string updatePath)
|
||||||
{
|
{
|
||||||
updatePath = null;
|
updatePath = null;
|
||||||
|
|
||||||
|
|
@ -138,38 +138,26 @@ 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;
|
// Load update information if exists.
|
||||||
|
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
||||||
if (updateStream == null)
|
if (File.Exists(titleUpdateMetadataPath))
|
||||||
{
|
{
|
||||||
// Load update information if exists.
|
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
|
||||||
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
|
if (File.Exists(updatePath))
|
||||||
if (File.Exists(titleUpdateMetadataPath))
|
|
||||||
{
|
{
|
||||||
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
|
IFileSystem updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem);
|
||||||
if (File.Exists(updatePath))
|
|
||||||
{
|
|
||||||
updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updatePath, fileSystem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updatePartitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(updateStream, false, fileSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatePartitionFileSystem != null)
|
foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel))
|
||||||
{
|
|
||||||
foreach ((ulong applicationTitleId, ContentMetaData content) in updatePartitionFileSystem.GetContentData(ContentMetaType.Patch, fileSystem, checkLevel))
|
|
||||||
{
|
|
||||||
if ((applicationTitleId & ~0x1FFFUL) != titleIdBase)
|
|
||||||
{
|
{
|
||||||
continue;
|
if ((applicationTitleId & ~0x1FFFUL) != titleIdBase)
|
||||||
}
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex);
|
updatePatchNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Program, programIndex);
|
||||||
updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
|
updateControlNca = content.GetNcaByType(fileSystem.KeySet, ContentType.Control, programIndex);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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, Stream updateStream = null)
|
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, ulong applicationId, out string errorMessage)
|
||||||
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 _, updateStream);
|
(Nca updatePatchNca, Nca updateControlNca) = mainNca.GetUpdateData(device.FileSystem, device.System.FsIntegrityCheckLevel, device.Configuration.UserChannelPersistence.Index, out string _);
|
||||||
|
|
||||||
if (updatePatchNca != null)
|
if (updatePatchNca != null)
|
||||||
{
|
{
|
||||||
|
|
@ -131,7 +131,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
||||||
{
|
{
|
||||||
if (downloadableContentNca.Enabled)
|
if (downloadableContentNca.Enabled)
|
||||||
{
|
{
|
||||||
device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, stream, downloadableContentNca.FullPath, System.IO.Path.GetExtension(downloadableContentContainer.ContainerPath));
|
device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -35,12 +35,6 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
public bool LoadXci(string path, ulong applicationId)
|
public bool LoadXci(string path, ulong applicationId)
|
||||||
{
|
{
|
||||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
return LoadXci(stream, 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());
|
||||||
|
|
||||||
if (!xci.HasPartition(XciPartitionType.Secure))
|
if (!xci.HasPartition(XciPartitionType.Secure))
|
||||||
|
|
@ -50,7 +44,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", updateStream);
|
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
|
|
@ -75,16 +69,10 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
public bool LoadNsp(string path, ulong applicationId)
|
public bool LoadNsp(string path, ulong applicationId)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
return LoadNsp(file, applicationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LoadNsp(Stream stream, ulong applicationId, Stream updateStream = null)
|
|
||||||
{
|
|
||||||
PartitionFileSystem partitionFileSystem = new();
|
PartitionFileSystem partitionFileSystem = new();
|
||||||
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure();
|
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||||
|
|
||||||
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, applicationId, out string errorMessage, "nsp", updateStream);
|
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
|
||||||
|
|
||||||
if (processResult.ProcessId == 0)
|
if (processResult.ProcessId == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -113,13 +101,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
public bool LoadNca(string path)
|
public bool LoadNca(string path)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||||
return LoadNca(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LoadNca(Stream ncaStream)
|
|
||||||
{
|
|
||||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, ncaStream.AsStorage(false));
|
|
||||||
|
|
||||||
ProcessResult processResult = nca.Load(_device, null, null);
|
ProcessResult processResult = nca.Load(_device, null, null);
|
||||||
|
|
||||||
|
|
@ -267,109 +249,5 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadNxo(Stream stream, bool isNro, string name)
|
|
||||||
{
|
|
||||||
var nacpData = new BlitStruct<ApplicationControlProperty>(1);
|
|
||||||
IFileSystem dummyExeFs = null;
|
|
||||||
Stream romfsStream = null;
|
|
||||||
|
|
||||||
string programName = "";
|
|
||||||
ulong programId = 0000000000000000;
|
|
||||||
|
|
||||||
// Load executable.
|
|
||||||
IExecutable executable;
|
|
||||||
|
|
||||||
if (isNro)
|
|
||||||
{
|
|
||||||
NroExecutable nro = new(stream.AsStorage());
|
|
||||||
|
|
||||||
executable = nro;
|
|
||||||
|
|
||||||
// Open RomFS if exists.
|
|
||||||
IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false);
|
|
||||||
romFsStorage.GetSize(out long romFsSize).ThrowIfFailure();
|
|
||||||
if (romFsSize != 0)
|
|
||||||
{
|
|
||||||
romfsStream = romFsStorage.AsStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Nacp if exists.
|
|
||||||
IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false);
|
|
||||||
nacpStorage.GetSize(out long nacpSize).ThrowIfFailure();
|
|
||||||
if (nacpSize != 0)
|
|
||||||
{
|
|
||||||
nacpStorage.Read(0, nacpData.ByteSpan);
|
|
||||||
|
|
||||||
programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(programName))
|
|
||||||
{
|
|
||||||
programName = Array.Find(nacpData.Value.Title.AsReadOnlySpan().ToArray(), x => x.Name[0] != 0).NameString.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nacpData.Value.PresenceGroupId != 0)
|
|
||||||
{
|
|
||||||
programId = nacpData.Value.PresenceGroupId;
|
|
||||||
}
|
|
||||||
else if (nacpData.Value.SaveDataOwnerId != 0)
|
|
||||||
{
|
|
||||||
programId = nacpData.Value.SaveDataOwnerId;
|
|
||||||
}
|
|
||||||
else if (nacpData.Value.AddOnContentBaseId != 0)
|
|
||||||
{
|
|
||||||
programId = nacpData.Value.AddOnContentBaseId - 0x1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add icon maybe ?
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
executable = new NsoExecutable(new LocalStorage(name, FileAccess.Read), programName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicitly null TitleId to disable the shader cache.
|
|
||||||
Graphics.Gpu.GraphicsConfig.TitleId = null;
|
|
||||||
_device.Gpu.HostInitalized.Set();
|
|
||||||
|
|
||||||
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
|
|
||||||
_device.System.KernelContext,
|
|
||||||
dummyExeFs.GetNpdm(),
|
|
||||||
nacpData,
|
|
||||||
diskCacheEnabled: false,
|
|
||||||
diskCacheSelector: null,
|
|
||||||
allowCodeMemoryForJit: true,
|
|
||||||
programName,
|
|
||||||
programId,
|
|
||||||
0,
|
|
||||||
null,
|
|
||||||
executable);
|
|
||||||
|
|
||||||
// Make sure the process id is valid.
|
|
||||||
if (processResult.ProcessId != 0)
|
|
||||||
{
|
|
||||||
// Load RomFS.
|
|
||||||
if (romfsStream != null)
|
|
||||||
{
|
|
||||||
_device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start process.
|
|
||||||
if (_processesByPid.TryAdd(processResult.ProcessId, processResult))
|
|
||||||
{
|
|
||||||
if (processResult.Start(_device))
|
|
||||||
{
|
|
||||||
_latestPid = processResult.ProcessId;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ using Ryujinx.HLE.Loaders.Processes;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.UI;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE
|
namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
|
|
@ -119,26 +117,6 @@ namespace Ryujinx.HLE
|
||||||
return Processes.LoadNxo(fileName);
|
return Processes.LoadNxo(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LoadXci(Stream xciStream, ulong applicationId = 0, Stream updateStream = null)
|
|
||||||
{
|
|
||||||
return Processes.LoadXci(xciStream, applicationId, updateStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LoadNca(Stream ncaStream)
|
|
||||||
{
|
|
||||||
return Processes.LoadNca(ncaStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LoadNsp(Stream nspStream, ulong applicationId = 0, Stream updateStream = null)
|
|
||||||
{
|
|
||||||
return Processes.LoadNsp(nspStream, applicationId, updateStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LoadProgram(Stream stream, bool isNro, string name)
|
|
||||||
{
|
|
||||||
return Processes.LoadNxo(stream, isNro, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool WaitFifo()
|
public bool WaitFifo()
|
||||||
{
|
{
|
||||||
return Gpu.GPFifo.WaitForCommands();
|
return Gpu.GPFifo.WaitForCommands();
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,16 @@ namespace Ryujinx.HLE.Utilities
|
||||||
{
|
{
|
||||||
FileStream file = File.OpenRead(path);
|
FileStream file = File.OpenRead(path);
|
||||||
|
|
||||||
return OpenApplicationFileSystem(file, Path.GetExtension(path).ToLower() == ".xci", fileSystem, throwOnFailure);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IFileSystem OpenApplicationFileSystem(Stream stream, bool isXci, VirtualFileSystem fileSystem, bool throwOnFailure = true)
|
|
||||||
{
|
|
||||||
IFileSystem partitionFileSystem;
|
IFileSystem partitionFileSystem;
|
||||||
|
|
||||||
if (isXci)
|
if (Path.GetExtension(path).ToLower() == ".xci")
|
||||||
{
|
{
|
||||||
partitionFileSystem = new Xci(fileSystem.KeySet, stream.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
partitionFileSystem = new Xci(fileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var pfsTemp = new PartitionFileSystem();
|
var pfsTemp = new PartitionFileSystem();
|
||||||
Result initResult = pfsTemp.Initialize(stream.AsStorage());
|
Result initResult = pfsTemp.Initialize(file.AsStorage());
|
||||||
|
|
||||||
if (throwOnFailure)
|
if (throwOnFailure)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue