ProcessLoader.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using LibHac.Common;
  2. using LibHac.Fs;
  3. using LibHac.Fs.Fsa;
  4. using LibHac.FsSystem;
  5. using LibHac.Ns;
  6. using LibHac.Tools.Fs;
  7. using LibHac.Tools.FsSystem;
  8. using LibHac.Tools.FsSystem.NcaUtils;
  9. using Ryujinx.Common;
  10. using Ryujinx.Common.Logging;
  11. using Ryujinx.Graphics.Gpu;
  12. using Ryujinx.HLE.Loaders.Executables;
  13. using Ryujinx.HLE.Loaders.Processes.Extensions;
  14. using System;
  15. using System.Collections.Concurrent;
  16. using System.IO;
  17. using Path = System.IO.Path;
  18. namespace Ryujinx.HLE.Loaders.Processes
  19. {
  20. public class ProcessLoader
  21. {
  22. private readonly Switch _device;
  23. private readonly ConcurrentDictionary<ulong, ProcessResult> _processesByPid;
  24. private ulong _latestPid;
  25. public ProcessResult ActiveApplication
  26. {
  27. get
  28. {
  29. if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
  30. throw new RyujinxException(
  31. $"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
  32. return value;
  33. }
  34. }
  35. public ProcessLoader(Switch device)
  36. {
  37. _device = device;
  38. _processesByPid = new ConcurrentDictionary<ulong, ProcessResult>();
  39. }
  40. public bool LoadXci(string path, ulong applicationId)
  41. {
  42. FileStream stream = new(path, FileMode.Open, FileAccess.Read);
  43. Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
  44. if (!xci.HasPartition(XciPartitionType.Secure))
  45. {
  46. Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI Secure partition");
  47. return false;
  48. }
  49. (bool success, ProcessResult processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
  50. if (!success)
  51. {
  52. Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
  53. return false;
  54. }
  55. if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
  56. {
  57. if (processResult.Start(_device))
  58. {
  59. _latestPid = processResult.ProcessId;
  60. TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
  61. return true;
  62. }
  63. }
  64. return false;
  65. }
  66. public bool LoadNsp(string path, ulong applicationId)
  67. {
  68. FileStream file = new(path, FileMode.Open, FileAccess.Read);
  69. PartitionFileSystem partitionFileSystem = new();
  70. partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
  71. (bool success, ProcessResult processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
  72. if (processResult.ProcessId == 0)
  73. {
  74. // This is not a normal NSP, it's actually a ExeFS as a NSP
  75. processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), 0, true);
  76. }
  77. if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
  78. {
  79. if (processResult.Start(_device))
  80. {
  81. _latestPid = processResult.ProcessId;
  82. TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
  83. return true;
  84. }
  85. }
  86. if (!success)
  87. {
  88. Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
  89. }
  90. return false;
  91. }
  92. public bool LoadNca(string path, BlitStruct<ApplicationControlProperty>? customNacpData = null)
  93. {
  94. FileStream file = new(path, FileMode.Open, FileAccess.Read);
  95. Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
  96. ProcessResult processResult = nca.Load(_device, null, null, customNacpData);
  97. if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
  98. {
  99. if (processResult.Start(_device))
  100. {
  101. // NOTE: Check if process is SystemApplicationId or ApplicationId
  102. if (processResult.ProgramId > 0x01000000000007FF)
  103. {
  104. _latestPid = processResult.ProcessId;
  105. TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
  106. }
  107. return true;
  108. }
  109. }
  110. return false;
  111. }
  112. public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
  113. {
  114. ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
  115. if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
  116. {
  117. if (processResult.Start(_device))
  118. {
  119. _latestPid = processResult.ProcessId;
  120. TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
  121. return true;
  122. }
  123. }
  124. return false;
  125. }
  126. public bool LoadNxo(string path)
  127. {
  128. BlitStruct<ApplicationControlProperty> nacpData = new(1);
  129. IFileSystem dummyExeFs = null;
  130. Stream romfsStream = null;
  131. string programName = string.Empty;
  132. ulong programId = 0000000000000000;
  133. // Load executable.
  134. IExecutable executable;
  135. if (Path.GetExtension(path).ToLower() == ".nro")
  136. {
  137. FileStream input = new(path, FileMode.Open);
  138. NroExecutable nro = new(input.AsStorage());
  139. executable = nro;
  140. // Open RomFS if exists.
  141. IStorage romFsStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.RomFs, false);
  142. romFsStorage.GetSize(out long romFsSize).ThrowIfFailure();
  143. if (romFsSize != 0)
  144. {
  145. romfsStream = romFsStorage.AsStream();
  146. }
  147. // Load Nacp if exists.
  148. IStorage nacpStorage = nro.OpenNroAssetSection(LibHac.Tools.Ro.NroAssetType.Nacp, false);
  149. nacpStorage.GetSize(out long nacpSize).ThrowIfFailure();
  150. if (nacpSize != 0)
  151. {
  152. nacpStorage.Read(0, nacpData.ByteSpan);
  153. programName = nacpData.Value.Title[(int)_device.System.State.DesiredTitleLanguage].NameString.ToString();
  154. if (string.IsNullOrWhiteSpace(programName))
  155. {
  156. programName = Array.Find(nacpData.Value.Title.ItemsRo.ToArray(), x => x.Name[0] != 0).NameString.ToString();
  157. }
  158. if (nacpData.Value.PresenceGroupId != 0)
  159. {
  160. programId = nacpData.Value.PresenceGroupId;
  161. TitleIDs.CurrentApplication.Value = programId.ToString("X16");
  162. }
  163. else if (nacpData.Value.SaveDataOwnerId != 0)
  164. {
  165. programId = nacpData.Value.SaveDataOwnerId;
  166. TitleIDs.CurrentApplication.Value = programId.ToString("X16");
  167. }
  168. else if (nacpData.Value.AddOnContentBaseId != 0)
  169. {
  170. programId = nacpData.Value.AddOnContentBaseId - 0x1000;
  171. TitleIDs.CurrentApplication.Value = programId.ToString("X16");
  172. }
  173. }
  174. // TODO: Add icon maybe ?
  175. }
  176. else
  177. {
  178. programName = Path.GetFileNameWithoutExtension(path);
  179. executable = new NsoExecutable(new LocalStorage(path, FileAccess.Read), programName);
  180. }
  181. // Explicitly null TitleId to disable the shader cache.
  182. GraphicsConfig.TitleId = null;
  183. _device.Gpu.HostInitalized.Set();
  184. ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
  185. _device.System.KernelContext,
  186. dummyExeFs.GetNpdm(),
  187. nacpData,
  188. diskCacheEnabled: false,
  189. diskCacheSelector: null,
  190. allowCodeMemoryForJit: true,
  191. programName,
  192. programId,
  193. 0,
  194. null,
  195. executable);
  196. // Make sure the process id is valid.
  197. if (processResult.ProcessId != 0)
  198. {
  199. // Load RomFS.
  200. if (romfsStream != null)
  201. {
  202. _device.Configuration.VirtualFileSystem.SetRomFs(processResult.ProcessId, romfsStream);
  203. }
  204. // Start process.
  205. if (_processesByPid.TryAdd(processResult.ProcessId, processResult))
  206. {
  207. if (processResult.Start(_device))
  208. {
  209. _latestPid = processResult.ProcessId;
  210. return true;
  211. }
  212. }
  213. }
  214. return false;
  215. }
  216. }
  217. }