android - move title updates support to SAF

This commit is contained in:
Emmanuel Hansen 2023-12-30 12:23:07 +00:00
parent 7955b4f287
commit f403484276
10 changed files with 165 additions and 128 deletions

View file

@ -20,7 +20,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
private static readonly DownloadableContentJsonSerializerContext _contentSerializerContext = 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, Stream stream, 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, out string errorMessage, string extension, Stream updateStream = null)
where TMetaData : PartitionFileSystemMetaCore<TFormat, THeader, TEntry>, new()
where TFormat : IPartitionFileSystemFormat
where THeader : unmanaged, IPartitionFileSystemHeader
@ -87,42 +87,21 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
{
// Clear the program index part.
titleIdBase &= ~0xFUL;
// Load update information if exists.
string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath))
PartitionFileSystem updatePartitionFileSystem = new();
if (updateStream != null)
{
string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
if (File.Exists(updatePath))
LoadUpdate(device, updateStream, ref updatePatchNca, ref updateControlNca, titleIdBase, updatePartitionFileSystem);
}
else
{
// Load update information if exists.
string titleUpdateMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath))
{
PartitionFileSystem updatePartitionFileSystem = new();
updatePartitionFileSystem.Initialize(new FileStream(updatePath, FileMode.Open, FileAccess.Read).AsStorage()).ThrowIfFailure();
device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem);
// TODO: This should use CNMT NCA instead.
foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca"))
string updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _titleSerializerContext.TitleUpdateMetadata).Selected;
if (File.Exists(updatePath))
{
Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath);
if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
{
continue;
}
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16"))
{
break;
}
if (nca.IsProgram())
{
updatePatchNca = nca;
}
else if (nca.IsControl())
{
updateControlNca = nca;
}
LoadUpdate(device, new FileStream(updatePath, FileMode.Open, FileAccess.Read), ref updatePatchNca, ref updateControlNca, titleIdBase, updatePartitionFileSystem);
}
}
}
@ -171,6 +150,38 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
errorMessage = "Unable to load: Could not find Main NCA";
return (false, ProcessResult.Failed);
static void LoadUpdate(Switch device, Stream updateStream, ref Nca updatePatchNca, ref Nca updateControlNca, ulong titleIdBase, PartitionFileSystem updatePartitionFileSystem)
{
updatePartitionFileSystem.Initialize(updateStream.AsStorage()).ThrowIfFailure();
device.Configuration.VirtualFileSystem.ImportTickets(updatePartitionFileSystem);
// TODO: This should use CNMT NCA instead.
foreach (DirectoryEntryEx fileEntry in updatePartitionFileSystem.EnumerateEntries("/", "*.nca"))
{
Nca nca = updatePartitionFileSystem.GetNca(device, fileEntry.FullPath);
if (nca.GetProgramIndex() != device.Configuration.UserChannelPersistence.Index)
{
continue;
}
if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleIdBase.ToString("x16"))
{
break;
}
if (nca.IsProgram())
{
updatePatchNca = nca;
}
else if (nca.IsControl())
{
updateControlNca = nca;
}
}
}
}
public static Nca GetNca(this IFileSystem fileSystem, Switch device, string path)

View file

@ -39,7 +39,7 @@ namespace Ryujinx.HLE.Loaders.Processes
return LoadXci(stream);
}
public bool LoadXci(Stream stream)
public bool LoadXci(Stream stream, 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, out string errorMessage, "xci");
(bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, stream, out string errorMessage, "xci", updateStream);
if (!success)
{
@ -79,12 +79,12 @@ namespace Ryujinx.HLE.Loaders.Processes
return LoadNsp(file);
}
public bool LoadNsp(Stream stream)
public bool LoadNsp(Stream stream, Stream updateStream = null)
{
PartitionFileSystem partitionFileSystem = new();
partitionFileSystem.Initialize(stream.AsStorage()).ThrowIfFailure();
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, out string errorMessage, "nsp");
(bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, stream, out string errorMessage, "nsp", updateStream);
if (processResult.ProcessId == 0)
{

View file

@ -93,7 +93,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);
}
@ -103,9 +103,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)