| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- using LibHac.Common;
- using LibHac.Common.Keys;
- using LibHac.Fs;
- using LibHac.Fs.Fsa;
- using LibHac.Ncm;
- using LibHac.Ns;
- using LibHac.Tools.FsSystem;
- using LibHac.Tools.FsSystem.NcaUtils;
- using Ryujinx.Ava.Common.Models;
- using Ryujinx.Common.Configuration;
- using Ryujinx.Common.Logging;
- using Ryujinx.Common.Utilities;
- using Ryujinx.HLE.FileSystem;
- using Ryujinx.HLE.Loaders.Processes.Extensions;
- using Ryujinx.HLE.Utilities;
- using Ryujinx.UI.Common.Configuration;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using ContentType = LibHac.Ncm.ContentType;
- using Path = System.IO.Path;
- using SpanHelpers = LibHac.Common.SpanHelpers;
- using TitleUpdateMetadata = Ryujinx.Common.Configuration.TitleUpdateMetadata;
- namespace Ryujinx.Ava.Utilities
- {
- public static class TitleUpdatesHelper
- {
- private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
- public static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase)
- {
- var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase);
- if (!File.Exists(titleUpdatesJsonPath))
- {
- return [];
- }
- try
- {
- var titleUpdateWindowData = JsonHelper.DeserializeFromFile(titleUpdatesJsonPath, _serializerContext.TitleUpdateMetadata);
- return LoadTitleUpdates(vfs, titleUpdateWindowData, applicationIdBase);
- }
- catch
- {
- Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {applicationIdBase:x16} at {titleUpdatesJsonPath}");
- return [];
- }
- }
- public static void SaveTitleUpdatesJson(ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates)
- {
- var titleUpdateWindowData = new TitleUpdateMetadata
- {
- Selected = string.Empty,
- Paths = [],
- };
- foreach ((TitleUpdateModel update, bool isSelected) in updates)
- {
- titleUpdateWindowData.Paths.Add(update.Path);
- if (isSelected)
- {
- if (!string.IsNullOrEmpty(titleUpdateWindowData.Selected))
- {
- Logger.Error?.Print(LogClass.Application,
- $"Tried to save two updates as 'IsSelected' for {applicationIdBase:x16}");
- return;
- }
- titleUpdateWindowData.Selected = update.Path;
- }
- }
- var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase);
- JsonHelper.SerializeToFile(titleUpdatesJsonPath, titleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
- }
- private static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase)
- {
- var result = new List<(TitleUpdateModel, bool IsSelected)>();
- IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
- ? IntegrityCheckLevel.ErrorOnInvalid
- : IntegrityCheckLevel.None;
- foreach (string path in titleUpdateMetadata.Paths)
- {
- if (!File.Exists(path))
- continue;
- try
- {
- using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, vfs);
- Dictionary<ulong, ContentMetaData> updates =
- pfs.GetContentData(ContentMetaType.Patch, vfs, checkLevel);
- if (!updates.TryGetValue(applicationIdBase, out ContentMetaData content))
- continue;
- Nca patchNca = content.GetNcaByType(vfs.KeySet, ContentType.Program);
- Nca controlNca = content.GetNcaByType(vfs.KeySet, ContentType.Control);
- if (controlNca is null || patchNca is null)
- continue;
- ApplicationControlProperty controlData = new();
- using UniqueRef<IFile> nacpFile = new();
- controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None)
- .OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
- nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None)
- .ThrowIfFailure();
- var displayVersion = controlData.DisplayVersionString.ToString();
- var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version,
- displayVersion, path);
- result.Add((update, path == titleUpdateMetadata.Selected));
- }
- catch (MissingKeyException exception)
- {
- Logger.Warning?.Print(LogClass.Application,
- $"Your key set is missing a key with the name: {exception.Name}");
- }
- catch (InvalidDataException)
- {
- Logger.Warning?.Print(LogClass.Application,
- $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Malformed File: {path}");
- }
- catch (IOException exception)
- {
- Logger.Warning?.Print(LogClass.Application, exception.Message);
- }
- catch (Exception exception)
- {
- Logger.Warning?.Print(LogClass.Application,
- $"The file encountered was not of a valid type. File: '{path}' Error: {exception}");
- }
- }
- return result;
- }
- private static string PathToGameUpdatesJson(ulong applicationIdBase)
- => Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "updates.json");
- }
- }
|