diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /Ryujinx.Ui.Common/App/ApplicationLibrary.cs | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'Ryujinx.Ui.Common/App/ApplicationLibrary.cs')
| -rw-r--r-- | Ryujinx.Ui.Common/App/ApplicationLibrary.cs | 923 |
1 files changed, 0 insertions, 923 deletions
diff --git a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/Ryujinx.Ui.Common/App/ApplicationLibrary.cs deleted file mode 100644 index b7b57f1a..00000000 --- a/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ /dev/null @@ -1,923 +0,0 @@ -using LibHac; -using LibHac.Common; -using LibHac.Common.Keys; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.Ui.Common.Configuration; -using Ryujinx.Ui.Common.Configuration.System; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Threading; -using Path = System.IO.Path; - -namespace Ryujinx.Ui.App.Common -{ - public class ApplicationLibrary - { - public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded; - public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated; - - private readonly byte[] _nspIcon; - private readonly byte[] _xciIcon; - private readonly byte[] _ncaIcon; - private readonly byte[] _nroIcon; - private readonly byte[] _nsoIcon; - - private readonly VirtualFileSystem _virtualFileSystem; - private Language _desiredTitleLanguage; - private CancellationTokenSource _cancellationToken; - - private static readonly ApplicationJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - private static readonly TitleUpdateMetadataJsonSerializerContext TitleSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public ApplicationLibrary(VirtualFileSystem virtualFileSystem) - { - _virtualFileSystem = virtualFileSystem; - - _nspIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSP.png"); - _xciIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_XCI.png"); - _ncaIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NCA.png"); - _nroIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NRO.png"); - _nsoIcon = GetResourceBytes("Ryujinx.Ui.Common.Resources.Icon_NSO.png"); - } - - private static byte[] GetResourceBytes(string resourceName) - { - Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); - byte[] resourceByteArray = new byte[resourceStream.Length]; - - resourceStream.Read(resourceByteArray); - - return resourceByteArray; - } - - public void CancelLoading() - { - _cancellationToken?.Cancel(); - } - - public static void ReadControlData(IFileSystem controlFs, Span<byte> outProperty) - { - using UniqueRef<IFile> controlFile = new(); - - controlFs.OpenFile(ref controlFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); - } - - public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage) - { - int numApplicationsFound = 0; - int numApplicationsLoaded = 0; - - _desiredTitleLanguage = desiredTitleLanguage; - - _cancellationToken = new CancellationTokenSource(); - - // Builds the applications list with paths to found applications - List<string> applications = new(); - - try - { - foreach (string appDir in appDirs) - { - if (_cancellationToken.Token.IsCancellationRequested) - { - return; - } - - if (!Directory.Exists(appDir)) - { - Logger.Warning?.Print(LogClass.Application, $"The \"game_dirs\" section in \"Config.json\" contains an invalid directory: \"{appDir}\""); - - continue; - } - - try - { - IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => - { - return - (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value) || - (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value) || - (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value) || - (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value) || - (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value) || - (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value); - }); - - foreach (string app in files) - { - if (_cancellationToken.Token.IsCancellationRequested) - { - return; - } - - var fileInfo = new FileInfo(app); - string extension = fileInfo.Extension.ToLower(); - - if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") - { - var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; - applications.Add(fullPath); - numApplicationsFound++; - } - } - } - catch (UnauthorizedAccessException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to get access to directory: \"{appDir}\""); - } - } - - // Loops through applications list, creating a struct and then firing an event containing the struct for each application - foreach (string applicationPath in applications) - { - if (_cancellationToken.Token.IsCancellationRequested) - { - return; - } - - double fileSize = new FileInfo(applicationPath).Length * 0.000000000931; - string titleName = "Unknown"; - string titleId = "0000000000000000"; - string developer = "Unknown"; - string version = "0"; - byte[] applicationIcon = null; - - BlitStruct<ApplicationControlProperty> controlHolder = new(1); - - try - { - string extension = Path.GetExtension(applicationPath).ToLower(); - - using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); - - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") - { - try - { - PartitionFileSystem pfs; - - bool isExeFs = false; - - if (extension == ".xci") - { - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - - // If the NSP doesn't have a main NCA, decrement the number of applications found and then continue to the next application. - bool hasMainNca = false; - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) - { - if (Path.GetExtension(fileEntry.FullPath).ToLower() == ".nca") - { - using UniqueRef<IFile> ncaFile = new(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - // Some main NCAs don't have a data partition, so check if the partition exists before opening it - if (nca.Header.ContentType == NcaContentType.Program && !(nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) - { - hasMainNca = true; - - break; - } - } - else if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") - { - isExeFs = true; - } - } - - if (!hasMainNca && !isExeFs) - { - numApplicationsFound--; - - continue; - } - } - - if (isExeFs) - { - applicationIcon = _nspIcon; - - using UniqueRef<IFile> npdmFile = new(); - - Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read); - - if (ResultFs.PathNotFound.Includes(result)) - { - Npdm npdm = new(npdmFile.Get.AsStream()); - - titleName = npdm.TitleName; - titleId = npdm.Aci0.TitleId.ToString("x16"); - } - } - else - { - GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out titleId); - - // Check if there is an update available. - if (IsUpdateApplied(titleId, out IFileSystem updatedControlFs)) - { - // Replace the original ControlFs by the updated one. - controlFs = updatedControlFs; - } - - ReadControlData(controlFs, controlHolder.ByteSpan); - - GetGameInformation(ref controlHolder.Value, out titleName, out _, out developer, out version); - - // Read the icon from the ControlFS and store it as a byte array - try - { - using UniqueRef<IFile> icon = new(); - - controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using MemoryStream stream = new(); - - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - catch (HorizonResultException) - { - foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) - { - if (entry.Name == "control.nacp") - { - continue; - } - - using var icon = new UniqueRef<IFile>(); - - controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using MemoryStream stream = new(); - - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - - if (applicationIcon != null) - { - break; - } - } - - applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; - } - } - } - catch (MissingKeyException exception) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - - Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); - } - catch (InvalidDataException) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - - Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}"); - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); - - numApplicationsFound--; - - continue; - } - } - else if (extension == ".nro") - { - BinaryReader reader = new(file); - - byte[] Read(long position, int size) - { - file.Seek(position, SeekOrigin.Begin); - - return reader.ReadBytes(size); - } - - try - { - file.Seek(24, SeekOrigin.Begin); - - int assetOffset = reader.ReadInt32(); - - if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") - { - byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); - - long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); - long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); - - ulong nacpOffset = reader.ReadUInt64(); - ulong nacpSize = reader.ReadUInt64(); - - // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); - - // Read the NACP data - Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan); - - GetGameInformation(ref controlHolder.Value, out titleName, out titleId, out developer, out version); - } - else - { - applicationIcon = _nroIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - - numApplicationsFound--; - - continue; - } - } - else if (extension == ".nca") - { - try - { - Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage()); - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.Header.ContentType != NcaContentType.Program || (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())) - { - numApplicationsFound--; - - continue; - } - } - catch (InvalidDataException) - { - Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}"); - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - - numApplicationsFound--; - - continue; - } - - applicationIcon = _ncaIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - // If its an NSO we just set defaults - else if (extension == ".nso") - { - applicationIcon = _nsoIcon; - titleName = Path.GetFileNameWithoutExtension(applicationPath); - } - } - catch (IOException exception) - { - Logger.Warning?.Print(LogClass.Application, exception.Message); - - numApplicationsFound--; - - continue; - } - - ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata => - { - appMetadata.Title = titleName; - }); - - if (appMetadata.LastPlayed != "Never") - { - if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) - { - Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); - - appMetadata.LastPlayed = "Never"; - } - else - { - appMetadata.LastPlayed = appMetadata.LastPlayed[..^3]; - } - } - - ApplicationData data = new() - { - Favorite = appMetadata.Favorite, - Icon = applicationIcon, - TitleName = titleName, - TitleId = titleId, - Developer = developer, - Version = version, - TimePlayed = ConvertSecondsToFormattedString(appMetadata.TimePlayed), - TimePlayedNum = appMetadata.TimePlayed, - LastPlayed = appMetadata.LastPlayed, - FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1), - FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + " MiB" : fileSize.ToString("0.##") + " GiB", - FileSizeBytes = fileSize, - Path = applicationPath, - ControlHolder = controlHolder - }; - - numApplicationsLoaded++; - - OnApplicationAdded(new ApplicationAddedEventArgs() - { - AppData = data - }); - - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() - { - NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded - }); - } - - OnApplicationCountUpdated(new ApplicationCountUpdatedEventArgs() - { - NumAppsFound = numApplicationsFound, - NumAppsLoaded = numApplicationsLoaded - }); - } - finally - { - _cancellationToken.Dispose(); - _cancellationToken = null; - } - } - - protected void OnApplicationAdded(ApplicationAddedEventArgs e) - { - ApplicationAdded?.Invoke(null, e); - } - - protected void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e) - { - ApplicationCountUpdated?.Invoke(null, e); - } - - private void GetControlFsAndTitleId(PartitionFileSystem pfs, out IFileSystem controlFs, out string titleId) - { - (_, _, Nca controlNca) = GetGameData(_virtualFileSystem, pfs, 0); - - // Return the ControlFS - controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - titleId = controlNca?.Header.TitleId.ToString("x16"); - } - - public ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null) - { - string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui"); - string metadataFile = Path.Combine(metadataFolder, "metadata.json"); - - ApplicationMetadata appMetadata; - - if (!File.Exists(metadataFile)) - { - Directory.CreateDirectory(metadataFolder); - - appMetadata = new ApplicationMetadata(); - - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); - } - - try - { - appMetadata = JsonHelper.DeserializeFromFile(metadataFile, SerializerContext.ApplicationMetadata); - } - catch (JsonException) - { - Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults."); - - appMetadata = new ApplicationMetadata(); - } - - if (modifyFunction != null) - { - modifyFunction(appMetadata); - - JsonHelper.SerializeToFile(metadataFile, appMetadata, SerializerContext.ApplicationMetadata); - } - - return appMetadata; - } - - public byte[] GetApplicationIcon(string applicationPath) - { - byte[] applicationIcon = null; - - try - { - // Look for icon only if applicationPath is not a directory - if (!Directory.Exists(applicationPath)) - { - string extension = Path.GetExtension(applicationPath).ToLower(); - - using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); - - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") - { - try - { - PartitionFileSystem pfs; - - bool isExeFs = false; - - if (extension == ".xci") - { - Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - - pfs = xci.OpenPartition(XciPartitionType.Secure); - } - else - { - pfs = new PartitionFileSystem(file.AsStorage()); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*")) - { - if (Path.GetFileNameWithoutExtension(fileEntry.FullPath) == "main") - { - isExeFs = true; - } - } - } - - if (isExeFs) - { - applicationIcon = _nspIcon; - } - else - { - // Store the ControlFS in variable called controlFs - GetControlFsAndTitleId(pfs, out IFileSystem controlFs, out _); - - // Read the icon from the ControlFS and store it as a byte array - try - { - using var icon = new UniqueRef<IFile>(); - - controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using MemoryStream stream = new(); - - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - catch (HorizonResultException) - { - foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*")) - { - if (entry.Name == "control.nacp") - { - continue; - } - - using var icon = new UniqueRef<IFile>(); - - controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using (MemoryStream stream = new()) - { - icon.Get.AsStream().CopyTo(stream); - applicationIcon = stream.ToArray(); - } - - if (applicationIcon != null) - { - break; - } - } - - applicationIcon ??= extension == ".xci" ? _xciIcon : _nspIcon; - } - } - } - catch (MissingKeyException) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - } - catch (InvalidDataException) - { - applicationIcon = extension == ".xci" ? _xciIcon : _nspIcon; - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}"); - } - } - else if (extension == ".nro") - { - BinaryReader reader = new(file); - - byte[] Read(long position, int size) - { - file.Seek(position, SeekOrigin.Begin); - - return reader.ReadBytes(size); - } - - try - { - file.Seek(24, SeekOrigin.Begin); - - int assetOffset = reader.ReadInt32(); - - if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET") - { - byte[] iconSectionInfo = Read(assetOffset + 8, 0x10); - - long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0); - long iconSize = BitConverter.ToInt64(iconSectionInfo, 8); - - // Reads and stores game icon as byte array - applicationIcon = Read(assetOffset + iconOffset, (int)iconSize); - } - else - { - applicationIcon = _nroIcon; - } - } - catch - { - Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}"); - } - } - else if (extension == ".nca") - { - applicationIcon = _ncaIcon; - } - // If its an NSO we just set defaults - else if (extension == ".nso") - { - applicationIcon = _nsoIcon; - } - } - } - catch(Exception) - { - Logger.Warning?.Print(LogClass.Application, $"Could not retrieve a valid icon for the app. Default icon will be used. Errored File: {applicationPath}"); - } - - return applicationIcon ?? _ncaIcon; - } - - private static string ConvertSecondsToFormattedString(double seconds) - { - System.TimeSpan time = System.TimeSpan.FromSeconds(seconds); - - string timeString; - if (time.Days != 0) - { - timeString = $"{time.Days}d {time.Hours:D2}h {time.Minutes:D2}m"; - } - else if (time.Hours != 0) - { - timeString = $"{time.Hours:D2}h {time.Minutes:D2}m"; - } - else if (time.Minutes != 0) - { - timeString = $"{time.Minutes:D2}m"; - } - else - { - timeString = "Never"; - } - - return timeString; - } - - private void GetGameInformation(ref ApplicationControlProperty controlData, out string titleName, out string titleId, out string publisher, out string version) - { - _ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage); - - if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage) - { - titleName = controlData.Title[(int)desiredTitleLanguage].NameString.ToString(); - publisher = controlData.Title[(int)desiredTitleLanguage].PublisherString.ToString(); - } - else - { - titleName = null; - publisher = null; - } - - if (string.IsNullOrWhiteSpace(titleName)) - { - foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) - { - if (!controlTitle.NameString.IsEmpty()) - { - titleName = controlTitle.NameString.ToString(); - - break; - } - } - } - - if (string.IsNullOrWhiteSpace(publisher)) - { - foreach (ref readonly var controlTitle in controlData.Title.ItemsRo) - { - if (!controlTitle.PublisherString.IsEmpty()) - { - publisher = controlTitle.PublisherString.ToString(); - - break; - } - } - } - - if (controlData.PresenceGroupId != 0) - { - titleId = controlData.PresenceGroupId.ToString("x16"); - } - else if (controlData.SaveDataOwnerId != 0) - { - titleId = controlData.SaveDataOwnerId.ToString(); - } - else if (controlData.AddOnContentBaseId != 0) - { - titleId = (controlData.AddOnContentBaseId - 0x1000).ToString("x16"); - } - else - { - titleId = "0000000000000000"; - } - - version = controlData.DisplayVersionString.ToString(); - } - - private bool IsUpdateApplied(string titleId, out IFileSystem updatedControlFs) - { - updatedControlFs = null; - - string updatePath = "(unknown)"; - - try - { - (Nca patchNca, Nca controlNca) = GetGameUpdateData(_virtualFileSystem, titleId, 0, out updatePath); - - if (patchNca != null && controlNca != null) - { - updatedControlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); - - return true; - } - } - catch (InvalidDataException) - { - Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {updatePath}"); - } - catch (MissingKeyException exception) - { - Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}. Errored File: {updatePath}"); - } - - return false; - } - - public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) - { - Nca mainNca = null; - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (mainNca, patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateDataFromPartition(VirtualFileSystem fileSystem, PartitionFileSystem pfs, string titleId, int programIndex) - { - Nca patchNca = null; - Nca controlNca = null; - - fileSystem.ImportTickets(pfs); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef<IFile>(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new Nca(fileSystem.KeySet, ncaFile.Release().AsStorage()); - - int ncaProgramIndex = (int)(nca.Header.TitleId & 0xF); - - if (ncaProgramIndex != programIndex) - { - continue; - } - - if ($"{nca.Header.TitleId.ToString("x16")[..^3]}000" != titleId) - { - break; - } - - if (nca.Header.ContentType == NcaContentType.Program) - { - patchNca = nca; - } - else if (nca.Header.ContentType == NcaContentType.Control) - { - controlNca = nca; - } - } - - return (patchNca, controlNca); - } - - public static (Nca patch, Nca control) GetGameUpdateData(VirtualFileSystem fileSystem, string titleId, int programIndex, out string updatePath) - { - updatePath = null; - - if (ulong.TryParse(titleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdBase)) - { - // Clear the program index part. - titleIdBase &= ~0xFUL; - - // Load update information if exists. - string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json"); - - if (File.Exists(titleUpdateMetadataPath)) - { - updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, TitleSerializerContext.TitleUpdateMetadata).Selected; - - if (File.Exists(updatePath)) - { - FileStream file = new FileStream(updatePath, FileMode.Open, FileAccess.Read); - PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage()); - - return GetGameUpdateDataFromPartition(fileSystem, nsp, titleIdBase.ToString("x16"), programIndex); - } - } - } - - return (null, null); - } - } -} - |
