Archived
1
0
forked from MeloNX/MeloNX

add stream based loaders

(cherry picked from commit e86dec9112b5e1b58a8241aadcf37bf2e3acd14c)
This commit is contained in:
Emmanuel Hansen 2023-07-02 16:49:14 +00:00
parent ca59c3f499
commit 6919b123b7
5 changed files with 178 additions and 22 deletions

View File

@ -40,12 +40,14 @@ namespace Ryujinx.HLE.FileSystem
private readonly struct AocItem
{
public readonly string ContainerPath;
public readonly Stream ContainerStream;
public readonly string NcaPath;
public readonly string Extension;
public AocItem(string containerPath, string ncaPath)
public AocItem(Stream containerStream, string ncaPath, string extension)
{
ContainerPath = containerPath;
ContainerStream = containerStream;
Extension = extension;
NcaPath = ncaPath;
}
}
@ -185,10 +187,10 @@ namespace Ryujinx.HLE.FileSystem
}
}
public void AddAocItem(ulong titleId, string containerPath, string ncaPath, bool mergedToContainer = false)
public void AddAocItem(ulong titleId, Stream containerStream, string ncaPath, string extension, bool mergedToContainer = false)
{
// TODO: Check Aoc version.
if (!AocData.TryAdd(titleId, new AocItem(containerPath, ncaPath)))
if (!AocData.TryAdd(titleId, new AocItem(containerStream, ncaPath, extension)))
{
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
}
@ -198,12 +200,20 @@ namespace Ryujinx.HLE.FileSystem
if (!mergedToContainer)
{
using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerPath, _virtualFileSystem);
using var pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(containerStream, extension == ".xci", _virtualFileSystem);
}
}
}
public void ClearAocData() => AocData.Clear();
public void ClearAocData()
{
foreach (var aoc in AocData)
{
aoc.Value.ContainerStream?.Dispose();
}
AocData.Clear();
}
public int GetAocCount() => AocData.Count;
@ -215,18 +225,17 @@ namespace Ryujinx.HLE.FileSystem
if (AocData.TryGetValue(aocTitleId, out AocItem aoc))
{
var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read);
using var ncaFile = new UniqueRef<IFile>();
switch (Path.GetExtension(aoc.ContainerPath))
switch (aoc.Extension)
{
case ".xci":
var xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
var xci = new Xci(_virtualFileSystem.KeySet, aoc.ContainerStream.AsStorage()).OpenPartition(XciPartitionType.Secure);
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read);
break;
case ".nsp":
var pfs = new PartitionFileSystem();
pfs.Initialize(file.AsStorage());
pfs.Initialize(aoc.ContainerStream.AsStorage());
pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
break;
default:

View File

@ -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, string path, ulong applicationId, out string errorMessage)
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)
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
where TFormat : IPartitionFileSystemFormat
where THeader : unmanaged, IPartitionFileSystemHeader
@ -131,7 +131,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
{
if (downloadableContentNca.Enabled)
{
device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, downloadableContentContainer.ContainerPath, downloadableContentNca.FullPath);
device.Configuration.ContentManager.AddAocItem(downloadableContentNca.TitleId, stream, downloadableContentNca.FullPath, System.IO.Path.GetExtension(downloadableContentContainer.ContainerPath));
}
}
else

View File

@ -35,6 +35,12 @@ namespace Ryujinx.HLE.Loaders.Processes
public bool LoadXci(string path, ulong applicationId)
{
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
return LoadXci(stream, applicationId);
}
public bool LoadXci(Stream stream, ulong applicationId)
{
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
if (!xci.HasPartition(XciPartitionType.Secure))
@ -44,7 +50,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return false;
}
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, applicationId, out string errorMessage, "xci");
if (!success)
{
@ -69,10 +75,16 @@ namespace Ryujinx.HLE.Loaders.Processes
public bool LoadNsp(string path, ulong applicationId)
{
FileStream file = new(path, FileMode.Open, FileAccess.Read);
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
return LoadNsp(file, applicationId);
}
public bool LoadNsp(Stream stream, ulong applicationId)
{
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure();
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, applicationId, out string errorMessage, "nsp");
if (processResult.ProcessId == 0)
{
@ -101,7 +113,13 @@ namespace Ryujinx.HLE.Loaders.Processes
public bool LoadNca(string path)
{
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);
@ -242,5 +260,108 @@ namespace Ryujinx.HLE.Loaders.Processes
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.ItemsRo.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,
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;
}
}
}

View File

@ -10,6 +10,7 @@ using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.HLE.UI;
using Ryujinx.Memory;
using System;
using System.IO;
namespace Ryujinx.HLE
{
@ -93,6 +94,26 @@ namespace Ryujinx.HLE
return Processes.LoadNxo(fileName);
}
public bool LoadXci(Stream xciStream)
{
return Processes.LoadXci(xciStream);
}
public bool LoadNca(Stream ncaStream)
{
return Processes.LoadNca(ncaStream);
}
public bool LoadNsp(Stream nspStream)
{
return Processes.LoadNsp(nspStream);
}
public bool LoadProgram(Stream stream, bool isNro, string name)
{
return Processes.LoadNxo(stream, isNro, name);
}
public bool WaitFifo()
{
return Gpu.GPFifo.WaitForCommands();

View File

@ -14,16 +14,21 @@ namespace Ryujinx.HLE.Utilities
{
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;
if (Path.GetExtension(path).ToLower() == ".xci")
if (isXci)
{
partitionFileSystem = new Xci(fileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
partitionFileSystem = new Xci(fileSystem.KeySet, stream.AsStorage()).OpenPartition(XciPartitionType.Secure);
}
else
{
var pfsTemp = new PartitionFileSystem();
Result initResult = pfsTemp.Initialize(file.AsStorage());
Result initResult = pfsTemp.Initialize(stream.AsStorage());
if (throwOnFailure)
{