Program.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. using ARMeilleure.Translation.PTC;
  2. using Gtk;
  3. using Ryujinx.Common.Configuration;
  4. using Ryujinx.Common.Logging;
  5. using Ryujinx.Common.SystemInfo;
  6. using Ryujinx.Configuration;
  7. using Ryujinx.Ui;
  8. using OpenTK;
  9. using System;
  10. using System.IO;
  11. using System.Reflection;
  12. namespace Ryujinx
  13. {
  14. class Program
  15. {
  16. public static string Version { get; private set; }
  17. public static string ConfigurationPath { get; set; }
  18. static void Main(string[] args)
  19. {
  20. // Parse Arguments
  21. string launchPath = null;
  22. string baseDirPath = null;
  23. for (int i = 0; i < args.Length; ++i)
  24. {
  25. string arg = args[i];
  26. if (arg == "-r" || arg == "--root-data-dir")
  27. {
  28. if (i + 1 >= args.Length)
  29. {
  30. Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
  31. continue;
  32. }
  33. baseDirPath = args[++i];
  34. }
  35. else if (launchPath == null)
  36. {
  37. launchPath = arg;
  38. }
  39. }
  40. Toolkit.Init(new ToolkitOptions
  41. {
  42. Backend = PlatformBackend.PreferNative,
  43. EnableHighResolution = true
  44. });
  45. Version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
  46. Console.Title = $"Ryujinx Console {Version}";
  47. string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
  48. Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
  49. // Hook unhandled exception and process exit events
  50. GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
  51. AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating);
  52. AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => ProgramExit();
  53. // Setup base data directory
  54. AppDataManager.Initialize(baseDirPath);
  55. // Initialize the configuration
  56. ConfigurationState.Initialize();
  57. // Initialize the logger system
  58. LoggerModule.Initialize();
  59. // Initialize Discord integration
  60. DiscordIntegrationModule.Initialize();
  61. string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
  62. string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, "Config.json");
  63. // Now load the configuration as the other subsystems are now registered
  64. if (File.Exists(localConfigurationPath))
  65. {
  66. ConfigurationPath = localConfigurationPath;
  67. ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(localConfigurationPath);
  68. ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
  69. }
  70. else if (File.Exists(appDataConfigurationPath))
  71. {
  72. ConfigurationPath = appDataConfigurationPath;
  73. ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(appDataConfigurationPath);
  74. ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
  75. }
  76. else
  77. {
  78. // No configuration, we load the default values and save it on disk
  79. ConfigurationPath = appDataConfigurationPath;
  80. ConfigurationState.Instance.LoadDefault();
  81. ConfigurationState.Instance.ToFileFormat().SaveConfig(appDataConfigurationPath);
  82. }
  83. PrintSystemInfo();
  84. Application.Init();
  85. bool hasGlobalProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
  86. bool hasAltProdKeys = !AppDataManager.IsCustomBasePath && File.Exists(Path.Combine(AppDataManager.KeysDirPathAlt, "prod.keys"));
  87. if (!hasGlobalProdKeys && !hasAltProdKeys && !Migration.IsMigrationNeeded())
  88. {
  89. GtkDialog.CreateWarningDialog("Key file was not found", "Please refer to `KEYS.md` for more info");
  90. }
  91. MainWindow mainWindow = new MainWindow();
  92. mainWindow.Show();
  93. if (launchPath != null)
  94. {
  95. mainWindow.LoadApplication(launchPath);
  96. }
  97. Application.Run();
  98. }
  99. private static void PrintSystemInfo()
  100. {
  101. Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
  102. Logger.Notice.Print(LogClass.Application, $"Operating System: {SystemInfo.Instance.OsDescription}");
  103. Logger.Notice.Print(LogClass.Application, $"CPU: {SystemInfo.Instance.CpuName}");
  104. Logger.Notice.Print(LogClass.Application, $"Total RAM: {SystemInfo.Instance.RamSizeInMB}");
  105. var enabledLogs = Logger.GetEnabledLevels();
  106. Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "<None>" : string.Join(", ", enabledLogs))}");
  107. if (AppDataManager.IsCustomBasePath)
  108. {
  109. Logger.Notice.Print(LogClass.Application, $"Custom Data Directory: {AppDataManager.BaseDirPath}");
  110. }
  111. }
  112. private static void ProcessUnhandledException(Exception e, bool isTerminating)
  113. {
  114. Ptc.Close();
  115. PtcProfiler.Stop();
  116. string message = $"Unhandled exception caught: {e}";
  117. Logger.Error?.PrintMsg(LogClass.Application, message);
  118. if (Logger.Error == null) Logger.Notice.PrintMsg(LogClass.Application, message);
  119. if (isTerminating)
  120. {
  121. ProgramExit();
  122. }
  123. }
  124. private static void ProgramExit()
  125. {
  126. Ptc.Dispose();
  127. PtcProfiler.Dispose();
  128. Logger.Shutdown();
  129. }
  130. }
  131. }