Horizon.cs 6.3 KB

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