Horizon.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using Ryujinx.HLE.Loaders.Executables;
  2. using Ryujinx.HLE.Logging;
  3. using Ryujinx.HLE.OsHle.Handles;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.IO;
  7. namespace Ryujinx.HLE.OsHle
  8. {
  9. public class Horizon : IDisposable
  10. {
  11. internal const int HidSize = 0x40000;
  12. internal const int FontSize = 0x50;
  13. private Switch Ns;
  14. private KProcessScheduler Scheduler;
  15. private ConcurrentDictionary<int, Process> Processes;
  16. public SystemStateMgr SystemState { get; private set; }
  17. internal MemoryAllocator Allocator { get; private set; }
  18. internal HSharedMem HidSharedMem { get; private set; }
  19. internal HSharedMem FontSharedMem { get; private set; }
  20. internal KEvent VsyncEvent { get; private set; }
  21. public Horizon(Switch Ns)
  22. {
  23. this.Ns = Ns;
  24. Scheduler = new KProcessScheduler(Ns.Log);
  25. Processes = new ConcurrentDictionary<int, Process>();
  26. SystemState = new SystemStateMgr();
  27. Allocator = new MemoryAllocator();
  28. HidSharedMem = new HSharedMem();
  29. FontSharedMem = new HSharedMem();
  30. VsyncEvent = new KEvent();
  31. }
  32. public void LoadCart(string ExeFsDir, string RomFsFile = null)
  33. {
  34. if (RomFsFile != null)
  35. {
  36. Ns.VFs.LoadRomFs(RomFsFile);
  37. }
  38. Process MainProcess = MakeProcess();
  39. void LoadNso(string FileName)
  40. {
  41. foreach (string File in Directory.GetFiles(ExeFsDir, FileName))
  42. {
  43. if (Path.GetExtension(File) != string.Empty)
  44. {
  45. continue;
  46. }
  47. Ns.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}...");
  48. using (FileStream Input = new FileStream(File, FileMode.Open))
  49. {
  50. string Name = Path.GetFileNameWithoutExtension(File);
  51. Nso Program = new Nso(Input, Name);
  52. MainProcess.LoadProgram(Program);
  53. }
  54. }
  55. }
  56. LoadNso("rtld");
  57. MainProcess.SetEmptyArgs();
  58. LoadNso("main");
  59. LoadNso("subsdk*");
  60. LoadNso("sdk");
  61. MainProcess.Run();
  62. }
  63. public void LoadProgram(string FilePath)
  64. {
  65. bool IsNro = Path.GetExtension(FilePath).ToLower() == ".nro";
  66. string Name = Path.GetFileNameWithoutExtension(FilePath);
  67. string SwitchFilePath = Ns.VFs.SystemPathToSwitchPath(FilePath);
  68. if (IsNro && (SwitchFilePath == null || !SwitchFilePath.StartsWith("sdmc:/")))
  69. {
  70. string SwitchPath = $"sdmc:/switch/{Name}{Homebrew.TemporaryNroSuffix}";
  71. string TempPath = Ns.VFs.SwitchPathToSystemPath(SwitchPath);
  72. string SwitchDir = Path.GetDirectoryName(TempPath);
  73. if (!Directory.Exists(SwitchDir))
  74. {
  75. Directory.CreateDirectory(SwitchDir);
  76. }
  77. File.Copy(FilePath, TempPath, true);
  78. FilePath = TempPath;
  79. }
  80. Process MainProcess = MakeProcess();
  81. using (FileStream Input = new FileStream(FilePath, FileMode.Open))
  82. {
  83. MainProcess.LoadProgram(IsNro
  84. ? (IExecutable)new Nro(Input, FilePath)
  85. : (IExecutable)new Nso(Input, FilePath));
  86. }
  87. MainProcess.SetEmptyArgs();
  88. MainProcess.Run(IsNro);
  89. }
  90. public void SignalVsync() => VsyncEvent.WaitEvent.Set();
  91. private Process MakeProcess()
  92. {
  93. Process Process;
  94. lock (Processes)
  95. {
  96. int ProcessId = 0;
  97. while (Processes.ContainsKey(ProcessId))
  98. {
  99. ProcessId++;
  100. }
  101. Process = new Process(Ns, Scheduler, ProcessId);
  102. Processes.TryAdd(ProcessId, Process);
  103. }
  104. InitializeProcess(Process);
  105. return Process;
  106. }
  107. private void InitializeProcess(Process Process)
  108. {
  109. Process.AppletState.SetFocus(true);
  110. }
  111. internal void ExitProcess(int ProcessId)
  112. {
  113. if (Processes.TryGetValue(ProcessId, out Process Process) && Process.NeedsHbAbi)
  114. {
  115. string NextNro = Homebrew.ReadHbAbiNextLoadPath(Process.Memory, Process.HbAbiDataPosition);
  116. Ns.Log.PrintInfo(LogClass.Loader, $"HbAbi NextLoadPath {NextNro}");
  117. if (NextNro == string.Empty)
  118. {
  119. NextNro = "sdmc:/hbmenu.nro";
  120. }
  121. NextNro = NextNro.Replace("sdmc:", string.Empty);
  122. NextNro = Ns.VFs.GetFullPath(Ns.VFs.GetSdCardPath(), NextNro);
  123. if (File.Exists(NextNro))
  124. {
  125. LoadProgram(NextNro);
  126. }
  127. }
  128. if (Processes.TryRemove(ProcessId, out Process))
  129. {
  130. Process.StopAllThreadsAsync();
  131. Process.Dispose();
  132. if (Processes.Count == 0)
  133. {
  134. Ns.OnFinish(EventArgs.Empty);
  135. }
  136. }
  137. }
  138. internal bool TryGetProcess(int ProcessId, out Process Process)
  139. {
  140. return Processes.TryGetValue(ProcessId, out Process);
  141. }
  142. public void Dispose()
  143. {
  144. Dispose(true);
  145. }
  146. protected virtual void Dispose(bool Disposing)
  147. {
  148. if (Disposing)
  149. {
  150. foreach (Process Process in Processes.Values)
  151. {
  152. Process.StopAllThreadsAsync();
  153. Process.Dispose();
  154. }
  155. VsyncEvent.Dispose();
  156. Scheduler.Dispose();
  157. }
  158. }
  159. }
  160. }