ApplicationData.cs
1 using LibHac.Common; 2 using LibHac.Fs; 3 using LibHac.Fs.Fsa; 4 using LibHac.FsSystem; 5 using LibHac.Loader; 6 using LibHac.Ns; 7 using LibHac.Tools.Fs; 8 using LibHac.Tools.FsSystem; 9 using LibHac.Tools.FsSystem.NcaUtils; 10 using Ryujinx.Common.Logging; 11 using Ryujinx.HLE.FileSystem; 12 using Ryujinx.HLE.Loaders.Processes.Extensions; 13 using Ryujinx.UI.Common.Helper; 14 using System; 15 using System.IO; 16 using System.Text.Json.Serialization; 17 18 namespace Ryujinx.UI.App.Common 19 { 20 public class ApplicationData 21 { 22 public bool Favorite { get; set; } 23 public byte[] Icon { get; set; } 24 public string Name { get; set; } = "Unknown"; 25 public ulong Id { get; set; } 26 public string Developer { get; set; } = "Unknown"; 27 public string Version { get; set; } = "0"; 28 public TimeSpan TimePlayed { get; set; } 29 public DateTime? LastPlayed { get; set; } 30 public string FileExtension { get; set; } 31 public long FileSize { get; set; } 32 public string Path { get; set; } 33 public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; } 34 35 public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); 36 37 public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed); 38 39 public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); 40 41 [JsonIgnore] public string IdString => Id.ToString("x16"); 42 43 [JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL; 44 45 [JsonIgnore] public string IdBaseString => IdBase.ToString("x16"); 46 47 public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath) 48 { 49 using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); 50 51 Nca mainNca = null; 52 Nca patchNca = null; 53 54 if (!System.IO.Path.Exists(titleFilePath)) 55 { 56 Logger.Error?.Print(LogClass.Application, $"File \"{titleFilePath}\" does not exist."); 57 return string.Empty; 58 } 59 60 string extension = System.IO.Path.GetExtension(titleFilePath).ToLower(); 61 62 if (extension is ".nsp" or ".xci") 63 { 64 IFileSystem pfs; 65 66 if (extension == ".xci") 67 { 68 Xci xci = new(virtualFileSystem.KeySet, file.AsStorage()); 69 70 pfs = xci.OpenPartition(XciPartitionType.Secure); 71 } 72 else 73 { 74 var pfsTemp = new PartitionFileSystem(); 75 pfsTemp.Initialize(file.AsStorage()).ThrowIfFailure(); 76 pfs = pfsTemp; 77 } 78 79 foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) 80 { 81 using var ncaFile = new UniqueRef<IFile>(); 82 83 pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); 84 85 Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); 86 87 if (nca.Header.ContentType != NcaContentType.Program) 88 { 89 continue; 90 } 91 92 int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); 93 94 if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) 95 { 96 patchNca = nca; 97 } 98 else 99 { 100 mainNca = nca; 101 } 102 } 103 } 104 else if (extension == ".nca") 105 { 106 mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage()); 107 } 108 109 if (mainNca == null) 110 { 111 Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file"); 112 113 return string.Empty; 114 } 115 116 (Nca updatePatchNca, _) = mainNca.GetUpdateData(virtualFileSystem, checkLevel, 0, out string _); 117 118 if (updatePatchNca != null) 119 { 120 patchNca = updatePatchNca; 121 } 122 123 IFileSystem codeFs = null; 124 125 if (patchNca == null) 126 { 127 if (mainNca.CanOpenSection(NcaSectionType.Code)) 128 { 129 codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); 130 } 131 } 132 else 133 { 134 if (patchNca.CanOpenSection(NcaSectionType.Code)) 135 { 136 codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); 137 } 138 } 139 140 if (codeFs == null) 141 { 142 Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA"); 143 144 return string.Empty; 145 } 146 147 const string MainExeFs = "main"; 148 149 if (!codeFs.FileExists($"/{MainExeFs}")) 150 { 151 Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS"); 152 153 return string.Empty; 154 } 155 156 using var nsoFile = new UniqueRef<IFile>(); 157 158 codeFs.OpenFile(ref nsoFile.Ref, $"/{MainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure(); 159 160 NsoReader reader = new(); 161 reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure(); 162 163 return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16]; 164 } 165 } 166 }