forked from MeloNX/MeloNX
add steam based loaders
This commit is contained in:
parent
56a6ac2a65
commit
07caf24936
@ -39,12 +39,14 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
private readonly struct AocItem
|
private readonly struct AocItem
|
||||||
{
|
{
|
||||||
public readonly string ContainerPath;
|
public readonly Stream ContainerStream;
|
||||||
public readonly string NcaPath;
|
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;
|
NcaPath = ncaPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fs must contain AOC nca files in its root
|
// fs must contain AOC nca files in its root
|
||||||
public void AddAocData(IFileSystem fs, string containerPath, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel)
|
public void AddAocData(IFileSystem fs, Stream containerStream, ulong aocBaseId, IntegrityCheckLevel integrityCheckLevel, string extension)
|
||||||
{
|
{
|
||||||
_virtualFileSystem.ImportTickets(fs);
|
_virtualFileSystem.ImportTickets(fs);
|
||||||
|
|
||||||
@ -220,14 +222,14 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower();
|
string ncaId = Convert.ToHexString(cnmt.ContentEntries[0].NcaId).ToLower();
|
||||||
|
|
||||||
AddAocItem(cnmt.TitleId, containerPath, $"/{ncaId}.nca", true);
|
AddAocItem(cnmt.TitleId, containerStream, $"/{ncaId}.nca", extension, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// 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}");
|
Logger.Warning?.Print(LogClass.Application, $"Duplicate AddOnContent detected. TitleId {titleId:X16}");
|
||||||
}
|
}
|
||||||
@ -237,16 +239,23 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
if (!mergedToContainer)
|
if (!mergedToContainer)
|
||||||
{
|
{
|
||||||
using FileStream fileStream = File.OpenRead(containerPath);
|
|
||||||
using PartitionFileSystem partitionFileSystem = new();
|
using PartitionFileSystem partitionFileSystem = new();
|
||||||
partitionFileSystem.Initialize(fileStream.AsStorage()).ThrowIfFailure();
|
partitionFileSystem.Initialize(containerStream.AsStorage()).ThrowIfFailure();
|
||||||
|
|
||||||
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearAocData() => AocData.Clear();
|
public void ClearAocData()
|
||||||
|
{
|
||||||
|
foreach (var aoc in AocData)
|
||||||
|
{
|
||||||
|
aoc.Value.ContainerStream?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
AocData.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public int GetAocCount() => AocData.Count;
|
public int GetAocCount() => AocData.Count;
|
||||||
|
|
||||||
@ -258,18 +267,17 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
if (AocData.TryGetValue(aocTitleId, out AocItem aoc))
|
if (AocData.TryGetValue(aocTitleId, out AocItem aoc))
|
||||||
{
|
{
|
||||||
var file = new FileStream(aoc.ContainerPath, FileMode.Open, FileAccess.Read);
|
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
switch (Path.GetExtension(aoc.ContainerPath))
|
switch (aoc.Extension)
|
||||||
{
|
{
|
||||||
case ".xci":
|
case ".xci":
|
||||||
var xci = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
var xci = new Xci(_virtualFileSystem.KeySet, aoc.ContainerStream.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||||
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
xci.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read);
|
||||||
break;
|
break;
|
||||||
case ".nsp":
|
case ".nsp":
|
||||||
var pfs = new PartitionFileSystem();
|
var pfs = new PartitionFileSystem();
|
||||||
pfs.Initialize(file.AsStorage());
|
pfs.Initialize(aoc.ContainerStream.AsStorage());
|
||||||
pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
private static readonly TitleUpdateMetadataJsonSerializerContext _titleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, string path, out string errorMessage)
|
internal static (bool, ProcessResult) TryLoad<TMetaData, TFormat, THeader, TEntry>(this PartitionFileSystemCore<TMetaData, TFormat, THeader, TEntry> partitionFileSystem, Switch device, , Stream stream, out string errorMessage, string extension)
|
||||||
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
|
||||||
@ -141,7 +141,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
// Load contained DownloadableContents.
|
// Load contained DownloadableContents.
|
||||||
// TODO: If we want to support multi-processes in future, we shouldn't clear AddOnContent data here.
|
// TODO: If we want to support multi-processes in future, we shouldn't clear AddOnContent data here.
|
||||||
device.Configuration.ContentManager.ClearAocData();
|
device.Configuration.ContentManager.ClearAocData();
|
||||||
device.Configuration.ContentManager.AddAocData(partitionFileSystem, path, mainNca.Header.TitleId, device.Configuration.FsIntegrityCheckLevel);
|
device.Configuration.ContentManager.AddAocData(partitionFileSystem, stream, mainNca.Header.TitleId, device.Configuration.FsIntegrityCheckLevel, extension);
|
||||||
|
|
||||||
// Load DownloadableContents.
|
// Load DownloadableContents.
|
||||||
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
|
||||||
@ -155,7 +155,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
|
|||||||
{
|
{
|
||||||
if (File.Exists(downloadableContentContainer.ContainerPath) && downloadableContentNca.Enabled)
|
if (File.Exists(downloadableContentContainer.ContainerPath) && 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
|
else
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,12 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
public bool LoadXci(string path)
|
public bool LoadXci(string path)
|
||||||
{
|
{
|
||||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||||
|
|
||||||
|
return LoadXci(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LoadXci(Stream stream)
|
||||||
|
{
|
||||||
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))
|
||||||
@ -44,7 +50,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, out string errorMessage);
|
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, out string errorMessage, "xci");
|
||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
@ -69,10 +75,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
public bool LoadNsp(string path)
|
public bool LoadNsp(string path)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
PartitionFileSystem partitionFileSystem = new();
|
|
||||||
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
return LoadNsp(file);
|
||||||
|
}
|
||||||
|
|
||||||
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, out string errorMessage);
|
public bool LoadNsp(Stream stream)
|
||||||
|
{
|
||||||
|
PartitionFileSystem partitionFileSystem = new();
|
||||||
|
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure();
|
||||||
|
|
||||||
|
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, out string errorMessage, "nsp");
|
||||||
|
|
||||||
if (processResult.ProcessId == 0)
|
if (processResult.ProcessId == 0)
|
||||||
{
|
{
|
||||||
@ -101,7 +113,13 @@ 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);
|
||||||
|
|
||||||
@ -241,5 +259,107 @@ 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.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,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ 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;
|
||||||
|
|
||||||
namespace Ryujinx.HLE
|
namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
@ -92,6 +93,26 @@ namespace Ryujinx.HLE
|
|||||||
return Processes.LoadNxo(fileName);
|
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()
|
public bool WaitFifo()
|
||||||
{
|
{
|
||||||
return Gpu.GPFifo.WaitForCommands();
|
return Gpu.GPFifo.WaitForCommands();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user