ApplicationData.cs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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 System;
  13. using System.Globalization;
  14. using System.IO;
  15. using System.Text.Json.Serialization;
  16. namespace Ryujinx.Ui.App.Common
  17. {
  18. public class ApplicationData
  19. {
  20. public bool Favorite { get; set; }
  21. public byte[] Icon { get; set; }
  22. public string TitleName { get; set; }
  23. public string TitleId { get; set; }
  24. public string Developer { get; set; }
  25. public string Version { get; set; }
  26. public string TimePlayed { get; set; }
  27. public double TimePlayedNum { get; set; }
  28. public DateTime? LastPlayed { get; set; }
  29. public string FileExtension { get; set; }
  30. public string FileSize { get; set; }
  31. public double FileSizeBytes { get; set; }
  32. public string Path { get; set; }
  33. public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
  34. [JsonIgnore]
  35. public string LastPlayedString
  36. {
  37. get
  38. {
  39. if (!LastPlayed.HasValue)
  40. {
  41. // TODO: maybe put localized string here instead of just "Never"
  42. return "Never";
  43. }
  44. return LastPlayed.Value.ToLocalTime().ToString(CultureInfo.CurrentCulture);
  45. }
  46. }
  47. public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
  48. {
  49. using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
  50. Nca mainNca = null;
  51. Nca patchNca = null;
  52. if (!System.IO.Path.Exists(titleFilePath))
  53. {
  54. Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}");
  55. return string.Empty;
  56. }
  57. string extension = System.IO.Path.GetExtension(titleFilePath).ToLower();
  58. if (extension is ".nsp" or ".xci")
  59. {
  60. PartitionFileSystem pfs;
  61. if (extension == ".xci")
  62. {
  63. Xci xci = new(virtualFileSystem.KeySet, file.AsStorage());
  64. pfs = xci.OpenPartition(XciPartitionType.Secure);
  65. }
  66. else
  67. {
  68. pfs = new PartitionFileSystem(file.AsStorage());
  69. }
  70. foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
  71. {
  72. using var ncaFile = new UniqueRef<IFile>();
  73. pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
  74. Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
  75. if (nca.Header.ContentType != NcaContentType.Program)
  76. {
  77. continue;
  78. }
  79. int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
  80. if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
  81. {
  82. patchNca = nca;
  83. }
  84. else
  85. {
  86. mainNca = nca;
  87. }
  88. }
  89. }
  90. else if (extension == ".nca")
  91. {
  92. mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage());
  93. }
  94. if (mainNca == null)
  95. {
  96. Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
  97. return string.Empty;
  98. }
  99. (Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
  100. if (updatePatchNca != null)
  101. {
  102. patchNca = updatePatchNca;
  103. }
  104. IFileSystem codeFs = null;
  105. if (patchNca == null)
  106. {
  107. if (mainNca.CanOpenSection(NcaSectionType.Code))
  108. {
  109. codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
  110. }
  111. }
  112. else
  113. {
  114. if (patchNca.CanOpenSection(NcaSectionType.Code))
  115. {
  116. codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
  117. }
  118. }
  119. if (codeFs == null)
  120. {
  121. Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
  122. return string.Empty;
  123. }
  124. const string MainExeFs = "main";
  125. if (!codeFs.FileExists($"/{MainExeFs}"))
  126. {
  127. Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS");
  128. return string.Empty;
  129. }
  130. using var nsoFile = new UniqueRef<IFile>();
  131. codeFs.OpenFile(ref nsoFile.Ref, $"/{MainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
  132. NsoReader reader = new NsoReader();
  133. reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
  134. return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
  135. }
  136. }
  137. }