MainWindow.cs 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941
  1. using Gtk;
  2. using LibHac.Common;
  3. using LibHac.Common.Keys;
  4. using LibHac.Ncm;
  5. using LibHac.Ns;
  6. using LibHac.Tools.FsSystem;
  7. using LibHac.Tools.FsSystem.NcaUtils;
  8. using Ryujinx.Audio.Backends.Dummy;
  9. using Ryujinx.Audio.Backends.OpenAL;
  10. using Ryujinx.Audio.Backends.SDL2;
  11. using Ryujinx.Audio.Backends.SoundIo;
  12. using Ryujinx.Audio.Integration;
  13. using Ryujinx.Common;
  14. using Ryujinx.Common.Configuration;
  15. using Ryujinx.Common.Configuration.Multiplayer;
  16. using Ryujinx.Common.Logging;
  17. using Ryujinx.Common.SystemInterop;
  18. using Ryujinx.Cpu;
  19. using Ryujinx.Graphics.GAL;
  20. using Ryujinx.Graphics.GAL.Multithreading;
  21. using Ryujinx.HLE.FileSystem;
  22. using Ryujinx.HLE.HOS;
  23. using Ryujinx.HLE.HOS.Services.Account.Acc;
  24. using Ryujinx.HLE.HOS.SystemState;
  25. using Ryujinx.Input.GTK3;
  26. using Ryujinx.Input.HLE;
  27. using Ryujinx.Input.SDL2;
  28. using Ryujinx.Modules;
  29. using Ryujinx.UI.App.Common;
  30. using Ryujinx.UI.Applet;
  31. using Ryujinx.UI.Common;
  32. using Ryujinx.UI.Common.Configuration;
  33. using Ryujinx.UI.Common.Helper;
  34. using Ryujinx.UI.Helper;
  35. using Ryujinx.UI.Widgets;
  36. using Ryujinx.UI.Windows;
  37. using Silk.NET.Vulkan;
  38. using SPB.Graphics.Vulkan;
  39. using System;
  40. using System.Diagnostics;
  41. using System.IO;
  42. using System.Reflection;
  43. using System.Threading;
  44. using System.Threading.Tasks;
  45. using GUI = Gtk.Builder.ObjectAttribute;
  46. using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
  47. namespace Ryujinx.UI
  48. {
  49. public class MainWindow : Window
  50. {
  51. private readonly VirtualFileSystem _virtualFileSystem;
  52. private readonly ContentManager _contentManager;
  53. private readonly AccountManager _accountManager;
  54. private readonly LibHacHorizonManager _libHacHorizonManager;
  55. private UserChannelPersistence _userChannelPersistence;
  56. private HLE.Switch _emulationContext;
  57. private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
  58. private readonly ApplicationLibrary _applicationLibrary;
  59. private readonly GtkHostUIHandler _uiHandler;
  60. private readonly AutoResetEvent _deviceExitStatus;
  61. private readonly ListStore _tableStore;
  62. private bool _updatingGameTable;
  63. private bool _gameLoaded;
  64. private bool _ending;
  65. private string _currentEmulatedGamePath = null;
  66. private string _lastScannedAmiiboId = "";
  67. private bool _lastScannedAmiiboShowAll = false;
  68. public RendererWidgetBase RendererWidget;
  69. public InputManager InputManager;
  70. public bool IsFocused;
  71. #pragma warning disable CS0169, CS0649, IDE0044, IDE0051 // Field is never assigned to, Add readonly modifier, Remove unused private member
  72. [GUI] public MenuItem ExitMenuItem;
  73. [GUI] public MenuItem UpdateMenuItem;
  74. [GUI] MenuBar _menuBar;
  75. [GUI] Box _footerBox;
  76. [GUI] Box _statusBar;
  77. [GUI] MenuItem _optionMenu;
  78. [GUI] MenuItem _manageUserProfiles;
  79. [GUI] MenuItem _fileMenu;
  80. [GUI] MenuItem _loadApplicationFile;
  81. [GUI] MenuItem _loadApplicationFolder;
  82. [GUI] MenuItem _appletMenu;
  83. [GUI] MenuItem _actionMenu;
  84. [GUI] MenuItem _pauseEmulation;
  85. [GUI] MenuItem _resumeEmulation;
  86. [GUI] MenuItem _stopEmulation;
  87. [GUI] MenuItem _simulateWakeUpMessage;
  88. [GUI] MenuItem _scanAmiibo;
  89. [GUI] MenuItem _takeScreenshot;
  90. [GUI] MenuItem _hideUI;
  91. [GUI] MenuItem _fullScreen;
  92. [GUI] CheckMenuItem _startFullScreen;
  93. [GUI] CheckMenuItem _showConsole;
  94. [GUI] CheckMenuItem _favToggle;
  95. [GUI] MenuItem _firmwareInstallDirectory;
  96. [GUI] MenuItem _firmwareInstallFile;
  97. [GUI] MenuItem _fileTypesSubMenu;
  98. [GUI] Label _fifoStatus;
  99. [GUI] CheckMenuItem _iconToggle;
  100. [GUI] CheckMenuItem _developerToggle;
  101. [GUI] CheckMenuItem _appToggle;
  102. [GUI] CheckMenuItem _timePlayedToggle;
  103. [GUI] CheckMenuItem _versionToggle;
  104. [GUI] CheckMenuItem _lastPlayedToggle;
  105. [GUI] CheckMenuItem _fileExtToggle;
  106. [GUI] CheckMenuItem _pathToggle;
  107. [GUI] CheckMenuItem _fileSizeToggle;
  108. [GUI] CheckMenuItem _nspShown;
  109. [GUI] CheckMenuItem _pfs0Shown;
  110. [GUI] CheckMenuItem _xciShown;
  111. [GUI] CheckMenuItem _ncaShown;
  112. [GUI] CheckMenuItem _nroShown;
  113. [GUI] CheckMenuItem _nsoShown;
  114. [GUI] Label _gpuBackend;
  115. [GUI] Label _dockedMode;
  116. [GUI] Label _aspectRatio;
  117. [GUI] Label _gameStatus;
  118. [GUI] TreeView _gameTable;
  119. [GUI] TreeSelection _gameTableSelection;
  120. [GUI] ScrolledWindow _gameTableWindow;
  121. [GUI] Label _gpuName;
  122. [GUI] Label _progressLabel;
  123. [GUI] Label _firmwareVersionLabel;
  124. [GUI] Gtk.ProgressBar _progressBar;
  125. [GUI] Box _viewBox;
  126. [GUI] Label _vSyncStatus;
  127. [GUI] Label _volumeStatus;
  128. [GUI] Box _listStatusBox;
  129. [GUI] Label _loadingStatusLabel;
  130. [GUI] Gtk.ProgressBar _loadingStatusBar;
  131. #pragma warning restore CS0649, IDE0044, CS0169, IDE0051
  132. public MainWindow() : this(new Builder("Ryujinx.Gtk3.UI.MainWindow.glade")) { }
  133. private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("_mainWin"))
  134. {
  135. builder.Autoconnect(this);
  136. // Apply custom theme if needed.
  137. ThemeHelper.ApplyTheme();
  138. SetWindowSizePosition();
  139. Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png");
  140. Title = $"Ryujinx {Program.Version}";
  141. // Hide emulation context status bar.
  142. _statusBar.Hide();
  143. // Instantiate HLE objects.
  144. _virtualFileSystem = VirtualFileSystem.CreateInstance();
  145. _libHacHorizonManager = new LibHacHorizonManager();
  146. _libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
  147. _libHacHorizonManager.InitializeArpServer();
  148. _libHacHorizonManager.InitializeBcatServer();
  149. _libHacHorizonManager.InitializeSystemClients();
  150. // Save data created before we supported extra data in directory save data will not work properly if
  151. // given empty extra data. Luckily some of that extra data can be created using the data from the
  152. // save data indexer, which should be enough to check access permissions for user saves.
  153. // Every single save data's extra data will be checked and fixed if needed each time the emulator is opened.
  154. // Consider removing this at some point in the future when we don't need to worry about old saves.
  155. VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient);
  156. _contentManager = new ContentManager(_virtualFileSystem);
  157. _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile);
  158. _userChannelPersistence = new UserChannelPersistence();
  159. // Instantiate GUI objects.
  160. _applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
  161. _uiHandler = new GtkHostUIHandler(this);
  162. _deviceExitStatus = new AutoResetEvent(false);
  163. WindowStateEvent += WindowStateEvent_Changed;
  164. DeleteEvent += Window_Close;
  165. FocusInEvent += MainWindow_FocusInEvent;
  166. FocusOutEvent += MainWindow_FocusOutEvent;
  167. _applicationLibrary.ApplicationAdded += Application_Added;
  168. _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
  169. _fileMenu.StateChanged += FileMenu_StateChanged;
  170. _actionMenu.StateChanged += ActionMenu_StateChanged;
  171. _optionMenu.StateChanged += OptionMenu_StateChanged;
  172. _gameTable.ButtonReleaseEvent += Row_Clicked;
  173. _fullScreen.Activated += FullScreen_Toggled;
  174. RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar;
  175. ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
  176. ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
  177. ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
  178. ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
  179. ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerMode;
  180. ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateMultiplayerLanInterfaceId;
  181. if (ConfigurationState.Instance.UI.StartFullscreen)
  182. {
  183. _startFullScreen.Active = true;
  184. }
  185. _showConsole.Active = ConfigurationState.Instance.UI.ShowConsole.Value;
  186. _showConsole.Visible = ConsoleHelper.SetConsoleWindowStateSupported;
  187. _actionMenu.Sensitive = false;
  188. _pauseEmulation.Sensitive = false;
  189. _resumeEmulation.Sensitive = false;
  190. _nspShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value;
  191. _pfs0Shown.Active = ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value;
  192. _xciShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value;
  193. _ncaShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value;
  194. _nroShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value;
  195. _nsoShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value;
  196. _nspShown.Toggled += NSP_Shown_Toggled;
  197. _pfs0Shown.Toggled += PFS0_Shown_Toggled;
  198. _xciShown.Toggled += XCI_Shown_Toggled;
  199. _ncaShown.Toggled += NCA_Shown_Toggled;
  200. _nroShown.Toggled += NRO_Shown_Toggled;
  201. _nsoShown.Toggled += NSO_Shown_Toggled;
  202. _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported;
  203. if (ConfigurationState.Instance.UI.GuiColumns.FavColumn)
  204. {
  205. _favToggle.Active = true;
  206. }
  207. if (ConfigurationState.Instance.UI.GuiColumns.IconColumn)
  208. {
  209. _iconToggle.Active = true;
  210. }
  211. if (ConfigurationState.Instance.UI.GuiColumns.AppColumn)
  212. {
  213. _appToggle.Active = true;
  214. }
  215. if (ConfigurationState.Instance.UI.GuiColumns.DevColumn)
  216. {
  217. _developerToggle.Active = true;
  218. }
  219. if (ConfigurationState.Instance.UI.GuiColumns.VersionColumn)
  220. {
  221. _versionToggle.Active = true;
  222. }
  223. if (ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn)
  224. {
  225. _timePlayedToggle.Active = true;
  226. }
  227. if (ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn)
  228. {
  229. _lastPlayedToggle.Active = true;
  230. }
  231. if (ConfigurationState.Instance.UI.GuiColumns.FileExtColumn)
  232. {
  233. _fileExtToggle.Active = true;
  234. }
  235. if (ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn)
  236. {
  237. _fileSizeToggle.Active = true;
  238. }
  239. if (ConfigurationState.Instance.UI.GuiColumns.PathColumn)
  240. {
  241. _pathToggle.Active = true;
  242. }
  243. _favToggle.Toggled += Fav_Toggled;
  244. _iconToggle.Toggled += Icon_Toggled;
  245. _appToggle.Toggled += App_Toggled;
  246. _developerToggle.Toggled += Developer_Toggled;
  247. _versionToggle.Toggled += Version_Toggled;
  248. _timePlayedToggle.Toggled += TimePlayed_Toggled;
  249. _lastPlayedToggle.Toggled += LastPlayed_Toggled;
  250. _fileExtToggle.Toggled += FileExt_Toggled;
  251. _fileSizeToggle.Toggled += FileSize_Toggled;
  252. _pathToggle.Toggled += Path_Toggled;
  253. _gameTable.Model = _tableStore = new ListStore(
  254. typeof(bool),
  255. typeof(Gdk.Pixbuf),
  256. typeof(string),
  257. typeof(string),
  258. typeof(string),
  259. typeof(string),
  260. typeof(string),
  261. typeof(string),
  262. typeof(string),
  263. typeof(string),
  264. typeof(BlitStruct<ApplicationControlProperty>));
  265. _tableStore.SetSortFunc(5, SortHelper.TimePlayedSort);
  266. _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort);
  267. _tableStore.SetSortFunc(8, SortHelper.FileSizeSort);
  268. int columnId = ConfigurationState.Instance.UI.ColumnSort.SortColumnId;
  269. bool ascending = ConfigurationState.Instance.UI.ColumnSort.SortAscending;
  270. _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending);
  271. _gameTable.EnableSearch = true;
  272. _gameTable.SearchColumn = 2;
  273. _gameTable.SearchEqualFunc = (model, col, key, iter) => !((string)model.GetValue(iter, col)).Contains(key, StringComparison.InvariantCultureIgnoreCase);
  274. _hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString());
  275. UpdateColumns();
  276. UpdateGameTable();
  277. ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) =>
  278. {
  279. if (args.OldValue != args.NewValue)
  280. {
  281. UpdateGameTable();
  282. }
  283. };
  284. Task.Run(RefreshFirmwareLabel);
  285. InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver());
  286. }
  287. private void UpdateMultiplayerLanInterfaceId(object sender, ReactiveEventArgs<string> args)
  288. {
  289. if (_emulationContext != null)
  290. {
  291. _emulationContext.Configuration.MultiplayerLanInterfaceId = args.NewValue;
  292. }
  293. }
  294. private void UpdateMultiplayerMode(object sender, ReactiveEventArgs<MultiplayerMode> args)
  295. {
  296. if (_emulationContext != null)
  297. {
  298. _emulationContext.Configuration.MultiplayerMode = args.NewValue;
  299. }
  300. }
  301. private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs<bool> args)
  302. {
  303. if (_emulationContext != null)
  304. {
  305. _emulationContext.Configuration.IgnoreMissingServices = args.NewValue;
  306. }
  307. }
  308. private void UpdateAspectRatioState(object sender, ReactiveEventArgs<AspectRatio> args)
  309. {
  310. if (_emulationContext != null)
  311. {
  312. _emulationContext.Configuration.AspectRatio = args.NewValue;
  313. }
  314. }
  315. private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
  316. {
  317. _emulationContext?.System.ChangeDockedModeState(e.NewValue);
  318. }
  319. private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
  320. {
  321. _emulationContext?.SetVolume(e.NewValue);
  322. }
  323. private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
  324. {
  325. _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen";
  326. }
  327. private void MainWindow_FocusOutEvent(object o, FocusOutEventArgs args)
  328. {
  329. IsFocused = false;
  330. }
  331. private void MainWindow_FocusInEvent(object o, FocusInEventArgs args)
  332. {
  333. IsFocused = true;
  334. }
  335. private void UpdateColumns()
  336. {
  337. foreach (TreeViewColumn column in _gameTable.Columns)
  338. {
  339. _gameTable.RemoveColumn(column);
  340. }
  341. CellRendererToggle favToggle = new();
  342. favToggle.Toggled += FavToggle_Toggled;
  343. if (ConfigurationState.Instance.UI.GuiColumns.FavColumn)
  344. {
  345. _gameTable.AppendColumn("Fav", favToggle, "active", 0);
  346. }
  347. if (ConfigurationState.Instance.UI.GuiColumns.IconColumn)
  348. {
  349. _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1);
  350. }
  351. if (ConfigurationState.Instance.UI.GuiColumns.AppColumn)
  352. {
  353. _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2);
  354. }
  355. if (ConfigurationState.Instance.UI.GuiColumns.DevColumn)
  356. {
  357. _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3);
  358. }
  359. if (ConfigurationState.Instance.UI.GuiColumns.VersionColumn)
  360. {
  361. _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4);
  362. }
  363. if (ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn)
  364. {
  365. _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5);
  366. }
  367. if (ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn)
  368. {
  369. _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6);
  370. }
  371. if (ConfigurationState.Instance.UI.GuiColumns.FileExtColumn)
  372. {
  373. _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7);
  374. }
  375. if (ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn)
  376. {
  377. _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8);
  378. }
  379. if (ConfigurationState.Instance.UI.GuiColumns.PathColumn)
  380. {
  381. _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9);
  382. }
  383. foreach (TreeViewColumn column in _gameTable.Columns)
  384. {
  385. switch (column.Title)
  386. {
  387. case "Fav":
  388. column.SortColumnId = 0;
  389. column.Clicked += Column_Clicked;
  390. break;
  391. case "Application":
  392. column.SortColumnId = 2;
  393. column.Clicked += Column_Clicked;
  394. break;
  395. case "Developer":
  396. column.SortColumnId = 3;
  397. column.Clicked += Column_Clicked;
  398. break;
  399. case "Version":
  400. column.SortColumnId = 4;
  401. column.Clicked += Column_Clicked;
  402. break;
  403. case "Time Played":
  404. column.SortColumnId = 5;
  405. column.Clicked += Column_Clicked;
  406. break;
  407. case "Last Played":
  408. column.SortColumnId = 6;
  409. column.Clicked += Column_Clicked;
  410. break;
  411. case "File Ext":
  412. column.SortColumnId = 7;
  413. column.Clicked += Column_Clicked;
  414. break;
  415. case "File Size":
  416. column.SortColumnId = 8;
  417. column.Clicked += Column_Clicked;
  418. break;
  419. case "Path":
  420. column.SortColumnId = 9;
  421. column.Clicked += Column_Clicked;
  422. break;
  423. }
  424. }
  425. }
  426. protected override void OnDestroyed()
  427. {
  428. InputManager.Dispose();
  429. }
  430. private void InitializeSwitchInstance()
  431. {
  432. _virtualFileSystem.ReloadKeySet();
  433. IRenderer renderer;
  434. if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan)
  435. {
  436. string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
  437. renderer = new Graphics.Vulkan.VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
  438. }
  439. else
  440. {
  441. renderer = new Graphics.OpenGL.OpenGLRenderer();
  442. }
  443. BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
  444. bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
  445. if (threadedGAL)
  446. {
  447. renderer = new ThreadedRenderer(renderer);
  448. }
  449. Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {threadedGAL}");
  450. IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver();
  451. if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2)
  452. {
  453. if (SDL2HardwareDeviceDriver.IsSupported)
  454. {
  455. deviceDriver = new SDL2HardwareDeviceDriver();
  456. }
  457. else
  458. {
  459. Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL.");
  460. if (OpenALHardwareDeviceDriver.IsSupported)
  461. {
  462. Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
  463. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
  464. SaveConfig();
  465. deviceDriver = new OpenALHardwareDeviceDriver();
  466. }
  467. else
  468. {
  469. Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO.");
  470. if (SoundIoHardwareDeviceDriver.IsSupported)
  471. {
  472. Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
  473. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
  474. SaveConfig();
  475. deviceDriver = new SoundIoHardwareDeviceDriver();
  476. }
  477. else
  478. {
  479. Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
  480. }
  481. }
  482. }
  483. }
  484. else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
  485. {
  486. if (SoundIoHardwareDeviceDriver.IsSupported)
  487. {
  488. deviceDriver = new SoundIoHardwareDeviceDriver();
  489. }
  490. else
  491. {
  492. Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, trying to fall back to SDL2.");
  493. if (SDL2HardwareDeviceDriver.IsSupported)
  494. {
  495. Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
  496. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
  497. SaveConfig();
  498. deviceDriver = new SDL2HardwareDeviceDriver();
  499. }
  500. else
  501. {
  502. Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL.");
  503. if (OpenALHardwareDeviceDriver.IsSupported)
  504. {
  505. Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
  506. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
  507. SaveConfig();
  508. deviceDriver = new OpenALHardwareDeviceDriver();
  509. }
  510. else
  511. {
  512. Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, falling back to dummy audio out.");
  513. }
  514. }
  515. }
  516. }
  517. else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl)
  518. {
  519. if (OpenALHardwareDeviceDriver.IsSupported)
  520. {
  521. deviceDriver = new OpenALHardwareDeviceDriver();
  522. }
  523. else
  524. {
  525. Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SDL2.");
  526. if (SDL2HardwareDeviceDriver.IsSupported)
  527. {
  528. Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
  529. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
  530. SaveConfig();
  531. deviceDriver = new SDL2HardwareDeviceDriver();
  532. }
  533. else
  534. {
  535. Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to SoundIO.");
  536. if (SoundIoHardwareDeviceDriver.IsSupported)
  537. {
  538. Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
  539. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
  540. SaveConfig();
  541. deviceDriver = new SoundIoHardwareDeviceDriver();
  542. }
  543. else
  544. {
  545. Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
  546. }
  547. }
  548. }
  549. }
  550. var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value
  551. ? HLE.MemoryConfiguration.MemoryConfiguration6GiB
  552. : HLE.MemoryConfiguration.MemoryConfiguration4GiB;
  553. IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
  554. HLE.HLEConfiguration configuration = new(_virtualFileSystem,
  555. _libHacHorizonManager,
  556. _contentManager,
  557. _accountManager,
  558. _userChannelPersistence,
  559. renderer,
  560. deviceDriver,
  561. memoryConfiguration,
  562. _uiHandler,
  563. (SystemLanguage)ConfigurationState.Instance.System.Language.Value,
  564. (RegionCode)ConfigurationState.Instance.System.Region.Value,
  565. ConfigurationState.Instance.Graphics.EnableVsync,
  566. ConfigurationState.Instance.System.EnableDockedMode,
  567. ConfigurationState.Instance.System.EnablePtc,
  568. ConfigurationState.Instance.System.EnableInternetAccess,
  569. fsIntegrityCheckLevel,
  570. ConfigurationState.Instance.System.FsGlobalAccessLogMode,
  571. ConfigurationState.Instance.System.SystemTimeOffset,
  572. ConfigurationState.Instance.System.TimeZone,
  573. ConfigurationState.Instance.System.MemoryManagerMode,
  574. ConfigurationState.Instance.System.IgnoreMissingServices,
  575. ConfigurationState.Instance.Graphics.AspectRatio,
  576. ConfigurationState.Instance.System.AudioVolume,
  577. ConfigurationState.Instance.System.UseHypervisor,
  578. ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
  579. ConfigurationState.Instance.Multiplayer.Mode);
  580. _emulationContext = new HLE.Switch(configuration);
  581. }
  582. private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk)
  583. {
  584. return new SurfaceKHR((ulong)((VulkanRenderer)RendererWidget).CreateWindowSurface(instance.Handle));
  585. }
  586. private void SetupProgressUIHandlers()
  587. {
  588. if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null)
  589. {
  590. _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler;
  591. _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler;
  592. }
  593. _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler;
  594. _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler;
  595. }
  596. private void ProgressHandler<T>(T state, int current, int total) where T : Enum
  597. {
  598. bool visible;
  599. string label;
  600. switch (state)
  601. {
  602. case LoadState ptcState:
  603. visible = ptcState != LoadState.Loaded;
  604. label = $"PTC : {current}/{total}";
  605. break;
  606. case ShaderCacheLoadingState shaderCacheState:
  607. visible = shaderCacheState != ShaderCacheLoadingState.Loaded;
  608. label = $"Shaders : {current}/{total}";
  609. break;
  610. default:
  611. throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}");
  612. }
  613. Application.Invoke(delegate
  614. {
  615. _loadingStatusLabel.Text = label;
  616. _loadingStatusBar.Fraction = total > 0 ? (double)current / total : 0;
  617. _loadingStatusBar.Visible = visible;
  618. _loadingStatusLabel.Visible = visible;
  619. });
  620. }
  621. public void UpdateGameTable()
  622. {
  623. if (_updatingGameTable || _gameLoaded)
  624. {
  625. return;
  626. }
  627. _updatingGameTable = true;
  628. _tableStore.Clear();
  629. Thread applicationLibraryThread = new(() =>
  630. {
  631. _applicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language);
  632. _updatingGameTable = false;
  633. })
  634. {
  635. Name = "GUI.ApplicationLibraryThread",
  636. IsBackground = true,
  637. };
  638. applicationLibraryThread.Start();
  639. }
  640. [Conditional("RELEASE")]
  641. public void PerformanceCheck()
  642. {
  643. if (ConfigurationState.Instance.Logger.EnableTrace.Value)
  644. {
  645. MessageDialog debugWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
  646. {
  647. Title = "Ryujinx - Warning",
  648. Text = "You have trace logging enabled, which is designed to be used by developers only.",
  649. SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?",
  650. };
  651. if (debugWarningDialog.Run() == (int)ResponseType.Yes)
  652. {
  653. ConfigurationState.Instance.Logger.EnableTrace.Value = false;
  654. SaveConfig();
  655. }
  656. debugWarningDialog.Dispose();
  657. }
  658. if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
  659. {
  660. MessageDialog shadersDumpWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
  661. {
  662. Title = "Ryujinx - Warning",
  663. Text = "You have shader dumping enabled, which is designed to be used by developers only.",
  664. SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?",
  665. };
  666. if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes)
  667. {
  668. ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
  669. SaveConfig();
  670. }
  671. shadersDumpWarningDialog.Dispose();
  672. }
  673. }
  674. private bool LoadApplication(string path, bool isFirmwareTitle)
  675. {
  676. SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
  677. if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError))
  678. {
  679. if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion))
  680. {
  681. string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})";
  682. ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run();
  683. if (responseDialog != ResponseType.Yes || !SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _))
  684. {
  685. UserErrorDialog.CreateUserErrorDialog(userError);
  686. return false;
  687. }
  688. // Tell the user that we installed a firmware for them.
  689. firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
  690. RefreshFirmwareLabel();
  691. message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start.";
  692. GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message);
  693. }
  694. else
  695. {
  696. UserErrorDialog.CreateUserErrorDialog(userError);
  697. return false;
  698. }
  699. }
  700. Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}");
  701. if (isFirmwareTitle)
  702. {
  703. Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA).");
  704. return _emulationContext.LoadNca(path);
  705. }
  706. if (Directory.Exists(path))
  707. {
  708. string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
  709. if (romFsFiles.Length == 0)
  710. {
  711. romFsFiles = Directory.GetFiles(path, "*.romfs");
  712. }
  713. if (romFsFiles.Length > 0)
  714. {
  715. Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS.");
  716. return _emulationContext.LoadCart(path, romFsFiles[0]);
  717. }
  718. Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
  719. return _emulationContext.LoadCart(path);
  720. }
  721. if (File.Exists(path))
  722. {
  723. switch (System.IO.Path.GetExtension(path).ToLowerInvariant())
  724. {
  725. case ".xci":
  726. Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
  727. return _emulationContext.LoadXci(path);
  728. case ".nca":
  729. Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
  730. return _emulationContext.LoadNca(path);
  731. case ".nsp":
  732. case ".pfs0":
  733. Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
  734. return _emulationContext.LoadNsp(path);
  735. default:
  736. Logger.Info?.Print(LogClass.Application, "Loading as Homebrew.");
  737. try
  738. {
  739. return _emulationContext.LoadProgram(path);
  740. }
  741. catch (ArgumentOutOfRangeException)
  742. {
  743. Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx.");
  744. return false;
  745. }
  746. }
  747. }
  748. Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
  749. return false;
  750. }
  751. public void RunApplication(string path, bool startFullscreen = false)
  752. {
  753. if (_gameLoaded)
  754. {
  755. GtkDialog.CreateInfoDialog("A game has already been loaded", "Please stop emulation or close the emulator before launching another game.");
  756. }
  757. else
  758. {
  759. PerformanceCheck();
  760. Logger.RestartTime();
  761. RendererWidget = CreateRendererWidget();
  762. SwitchToRenderWidget(startFullscreen);
  763. InitializeSwitchInstance();
  764. UpdateGraphicsConfig();
  765. bool isFirmwareTitle = false;
  766. if (path.StartsWith("@SystemContent"))
  767. {
  768. path = VirtualFileSystem.SwitchPathToSystemPath(path);
  769. isFirmwareTitle = true;
  770. }
  771. if (!LoadApplication(path, isFirmwareTitle))
  772. {
  773. _emulationContext.Dispose();
  774. SwitchToGameTable();
  775. return;
  776. }
  777. SetupProgressUIHandlers();
  778. _currentEmulatedGamePath = path;
  779. _deviceExitStatus.Reset();
  780. Thread windowThread = new(CreateGameWindow)
  781. {
  782. Name = "GUI.WindowThread",
  783. };
  784. windowThread.Start();
  785. _gameLoaded = true;
  786. _actionMenu.Sensitive = true;
  787. UpdateMenuItem.Sensitive = false;
  788. _lastScannedAmiiboId = "";
  789. _firmwareInstallFile.Sensitive = false;
  790. _firmwareInstallDirectory.Sensitive = false;
  791. DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText,
  792. _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString());
  793. ApplicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata =>
  794. {
  795. appMetadata.UpdatePreGame();
  796. });
  797. }
  798. }
  799. private RendererWidgetBase CreateRendererWidget()
  800. {
  801. if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan)
  802. {
  803. return new VulkanRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
  804. }
  805. else
  806. {
  807. return new OpenGLRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
  808. }
  809. }
  810. private void SwitchToRenderWidget(bool startFullscreen = false)
  811. {
  812. _viewBox.Remove(_gameTableWindow);
  813. RendererWidget.Expand = true;
  814. _viewBox.Child = RendererWidget;
  815. RendererWidget.ShowAll();
  816. EditFooterForGameRenderer();
  817. if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  818. {
  819. ToggleExtraWidgets(false);
  820. }
  821. else if (startFullscreen || ConfigurationState.Instance.UI.StartFullscreen.Value)
  822. {
  823. FullScreen_Toggled(null, null);
  824. }
  825. }
  826. private void SwitchToGameTable()
  827. {
  828. if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  829. {
  830. ToggleExtraWidgets(true);
  831. }
  832. RendererWidget.Exit();
  833. if (RendererWidget.Window != Window && RendererWidget.Window != null)
  834. {
  835. RendererWidget.Window.Dispose();
  836. }
  837. RendererWidget.Dispose();
  838. if (OperatingSystem.IsWindows())
  839. {
  840. _windowsMultimediaTimerResolution?.Dispose();
  841. _windowsMultimediaTimerResolution = null;
  842. }
  843. DisplaySleep.Restore();
  844. _viewBox.Remove(RendererWidget);
  845. _viewBox.Add(_gameTableWindow);
  846. _gameTableWindow.Expand = true;
  847. Window.Title = $"Ryujinx {Program.Version}";
  848. _emulationContext = null;
  849. _gameLoaded = false;
  850. RendererWidget = null;
  851. DiscordIntegrationModule.SwitchToMainMenu();
  852. RecreateFooterForMenu();
  853. UpdateColumns();
  854. UpdateGameTable();
  855. RefreshFirmwareLabel();
  856. HandleRelaunch();
  857. }
  858. private void CreateGameWindow()
  859. {
  860. if (OperatingSystem.IsWindows())
  861. {
  862. _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
  863. }
  864. DisplaySleep.Prevent();
  865. RendererWidget.Initialize(_emulationContext);
  866. RendererWidget.WaitEvent.WaitOne();
  867. RendererWidget.Start();
  868. _emulationContext.Dispose();
  869. _deviceExitStatus.Set();
  870. // NOTE: Everything that is here will not be executed when you close the UI.
  871. Application.Invoke(delegate
  872. {
  873. SwitchToGameTable();
  874. });
  875. }
  876. private void RecreateFooterForMenu()
  877. {
  878. _listStatusBox.Show();
  879. _statusBar.Hide();
  880. }
  881. private void EditFooterForGameRenderer()
  882. {
  883. _listStatusBox.Hide();
  884. _statusBar.Show();
  885. }
  886. public void ToggleExtraWidgets(bool show)
  887. {
  888. if (RendererWidget != null)
  889. {
  890. if (show)
  891. {
  892. _menuBar.ShowAll();
  893. _footerBox.Show();
  894. _statusBar.Show();
  895. }
  896. else
  897. {
  898. _menuBar.Hide();
  899. _footerBox.Hide();
  900. }
  901. }
  902. }
  903. private void UpdateGameMetadata(string titleId)
  904. {
  905. if (_gameLoaded)
  906. {
  907. ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
  908. {
  909. appMetadata.UpdatePostGame();
  910. });
  911. }
  912. }
  913. public static void UpdateGraphicsConfig()
  914. {
  915. int resScale = ConfigurationState.Instance.Graphics.ResScale;
  916. float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
  917. Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale;
  918. Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
  919. Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
  920. Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
  921. Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
  922. Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
  923. }
  924. public void UpdateInternetAccess()
  925. {
  926. if (_gameLoaded)
  927. {
  928. _emulationContext.Configuration.EnableInternetAccess = ConfigurationState.Instance.System.EnableInternetAccess.Value;
  929. }
  930. }
  931. public static void SaveConfig()
  932. {
  933. ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
  934. }
  935. private void End()
  936. {
  937. if (_ending)
  938. {
  939. return;
  940. }
  941. _ending = true;
  942. if (_emulationContext != null)
  943. {
  944. UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText);
  945. if (RendererWidget != null)
  946. {
  947. // We tell the widget that we are exiting.
  948. RendererWidget.Exit();
  949. // Wait for the other thread to dispose the HLE context before exiting.
  950. _deviceExitStatus.WaitOne();
  951. RendererWidget.Dispose();
  952. }
  953. }
  954. Dispose();
  955. Program.Exit();
  956. Application.Quit();
  957. }
  958. //
  959. // Events
  960. //
  961. private void Application_Added(object sender, ApplicationAddedEventArgs args)
  962. {
  963. Application.Invoke(delegate
  964. {
  965. _tableStore.AppendValues(
  966. args.AppData.Favorite,
  967. new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
  968. $"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
  969. args.AppData.Developer,
  970. args.AppData.Version,
  971. args.AppData.TimePlayedString,
  972. args.AppData.LastPlayedString,
  973. args.AppData.FileExtension,
  974. args.AppData.FileSizeString,
  975. args.AppData.Path,
  976. args.AppData.ControlHolder);
  977. });
  978. }
  979. private void ApplicationCount_Updated(object sender, ApplicationCountUpdatedEventArgs args)
  980. {
  981. Application.Invoke(delegate
  982. {
  983. _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
  984. float barValue = 0;
  985. if (args.NumAppsFound != 0)
  986. {
  987. barValue = (float)args.NumAppsLoaded / args.NumAppsFound;
  988. }
  989. _progressBar.Fraction = barValue;
  990. // Reset the vertical scrollbar to the top when titles finish loading
  991. if (args.NumAppsLoaded == args.NumAppsFound)
  992. {
  993. _gameTableWindow.Vadjustment.Value = 0;
  994. }
  995. });
  996. }
  997. private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
  998. {
  999. Application.Invoke(delegate
  1000. {
  1001. _gameStatus.Text = args.GameStatus;
  1002. _fifoStatus.Text = args.FifoStatus;
  1003. _gpuName.Text = args.GpuName;
  1004. _dockedMode.Text = args.DockedMode;
  1005. _aspectRatio.Text = args.AspectRatio;
  1006. _gpuBackend.Text = args.GpuBackend;
  1007. _volumeStatus.Text = GetVolumeLabelText(args.Volume);
  1008. if (args.VSyncEnabled)
  1009. {
  1010. _vSyncStatus.Attributes = new Pango.AttrList();
  1011. _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(11822, 60138, 51657));
  1012. }
  1013. else
  1014. {
  1015. _vSyncStatus.Attributes = new Pango.AttrList();
  1016. _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(ushort.MaxValue, 17733, 21588));
  1017. }
  1018. });
  1019. }
  1020. private void FavToggle_Toggled(object sender, ToggledArgs args)
  1021. {
  1022. _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path));
  1023. string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
  1024. bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0);
  1025. _tableStore.SetValue(treeIter, 0, newToggleValue);
  1026. ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
  1027. {
  1028. appMetadata.Favorite = newToggleValue;
  1029. });
  1030. }
  1031. private void Column_Clicked(object sender, EventArgs args)
  1032. {
  1033. TreeViewColumn column = (TreeViewColumn)sender;
  1034. ConfigurationState.Instance.UI.ColumnSort.SortColumnId.Value = column.SortColumnId;
  1035. ConfigurationState.Instance.UI.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending;
  1036. SaveConfig();
  1037. }
  1038. private void Row_Activated(object sender, RowActivatedArgs args)
  1039. {
  1040. _gameTableSelection.GetSelected(out TreeIter treeIter);
  1041. string path = (string)_tableStore.GetValue(treeIter, 9);
  1042. RunApplication(path);
  1043. }
  1044. private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args)
  1045. {
  1046. _emulationContext.EnableDeviceVsync = !_emulationContext.EnableDeviceVsync;
  1047. Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {_emulationContext.EnableDeviceVsync}");
  1048. }
  1049. private void DockedMode_Clicked(object sender, ButtonReleaseEventArgs args)
  1050. {
  1051. ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
  1052. }
  1053. private static string GetVolumeLabelText(float volume)
  1054. {
  1055. string icon = volume == 0 ? "🔇" : "🔊";
  1056. return $"{icon} {(int)(volume * 100)}%";
  1057. }
  1058. private void VolumeStatus_Clicked(object sender, ButtonReleaseEventArgs args)
  1059. {
  1060. if (_emulationContext != null)
  1061. {
  1062. if (_emulationContext.IsAudioMuted())
  1063. {
  1064. _emulationContext.SetVolume(ConfigurationState.Instance.System.AudioVolume);
  1065. }
  1066. else
  1067. {
  1068. _emulationContext.SetVolume(0);
  1069. }
  1070. }
  1071. }
  1072. private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args)
  1073. {
  1074. AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
  1075. ConfigurationState.Instance.Graphics.AspectRatio.Value = ((int)aspectRatio + 1) > Enum.GetNames<AspectRatio>().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
  1076. }
  1077. private void Row_Clicked(object sender, ButtonReleaseEventArgs args)
  1078. {
  1079. if (args.Event.Button != 3 /* Right Click */)
  1080. {
  1081. return;
  1082. }
  1083. _gameTableSelection.GetSelected(out TreeIter treeIter);
  1084. if (treeIter.UserData == IntPtr.Zero)
  1085. {
  1086. return;
  1087. }
  1088. string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
  1089. string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
  1090. string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
  1091. BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
  1092. _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, titleFilePath, titleName, titleId, controlData);
  1093. }
  1094. private void Load_Application_File(object sender, EventArgs args)
  1095. {
  1096. using FileChooserNative fileChooser = new("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel");
  1097. FileFilter filter = new()
  1098. {
  1099. Name = "Switch Executables",
  1100. };
  1101. filter.AddPattern("*.xci");
  1102. filter.AddPattern("*.nsp");
  1103. filter.AddPattern("*.pfs0");
  1104. filter.AddPattern("*.nca");
  1105. filter.AddPattern("*.nro");
  1106. filter.AddPattern("*.nso");
  1107. fileChooser.AddFilter(filter);
  1108. if (fileChooser.Run() == (int)ResponseType.Accept)
  1109. {
  1110. RunApplication(fileChooser.Filename);
  1111. }
  1112. }
  1113. private void Load_Application_Folder(object sender, EventArgs args)
  1114. {
  1115. using FileChooserNative fileChooser = new("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel");
  1116. if (fileChooser.Run() == (int)ResponseType.Accept)
  1117. {
  1118. RunApplication(fileChooser.Filename);
  1119. }
  1120. }
  1121. private void FileMenu_StateChanged(object o, StateChangedArgs args)
  1122. {
  1123. _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3;
  1124. _loadApplicationFile.Sensitive = _emulationContext == null;
  1125. _loadApplicationFolder.Sensitive = _emulationContext == null;
  1126. }
  1127. private void Load_Mii_Edit_Applet(object sender, EventArgs args)
  1128. {
  1129. string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
  1130. RunApplication(contentPath);
  1131. }
  1132. private void Open_Ryu_Folder(object sender, EventArgs args)
  1133. {
  1134. OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
  1135. }
  1136. private void OpenLogsFolder_Pressed(object sender, EventArgs args)
  1137. {
  1138. string logPath = AppDataManager.GetOrCreateLogsDir();
  1139. if (!string.IsNullOrEmpty(logPath))
  1140. {
  1141. OpenHelper.OpenFolder(logPath);
  1142. }
  1143. }
  1144. private void Exit_Pressed(object sender, EventArgs args)
  1145. {
  1146. if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
  1147. {
  1148. SaveWindowSizePosition();
  1149. End();
  1150. }
  1151. }
  1152. private void Window_Close(object sender, DeleteEventArgs args)
  1153. {
  1154. if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
  1155. {
  1156. SaveWindowSizePosition();
  1157. End();
  1158. }
  1159. else
  1160. {
  1161. args.RetVal = true;
  1162. }
  1163. }
  1164. private void SetWindowSizePosition()
  1165. {
  1166. DefaultWidth = ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth;
  1167. DefaultHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight;
  1168. Move(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX, ConfigurationState.Instance.UI.WindowStartup.WindowPositionY);
  1169. if (ConfigurationState.Instance.UI.WindowStartup.WindowMaximized)
  1170. {
  1171. Maximize();
  1172. }
  1173. }
  1174. private void SaveWindowSizePosition()
  1175. {
  1176. GetSize(out int windowWidth, out int windowHeight);
  1177. GetPosition(out int windowXPos, out int windowYPos);
  1178. ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = IsMaximized;
  1179. ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = windowWidth;
  1180. ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = windowHeight;
  1181. ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = windowXPos;
  1182. ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = windowYPos;
  1183. SaveConfig();
  1184. }
  1185. private void StopEmulation_Pressed(object sender, EventArgs args)
  1186. {
  1187. if (_emulationContext != null)
  1188. {
  1189. UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText);
  1190. }
  1191. _pauseEmulation.Sensitive = false;
  1192. _resumeEmulation.Sensitive = false;
  1193. UpdateMenuItem.Sensitive = true;
  1194. RendererWidget?.Exit();
  1195. }
  1196. private void PauseEmulation_Pressed(object sender, EventArgs args)
  1197. {
  1198. _pauseEmulation.Sensitive = false;
  1199. _resumeEmulation.Sensitive = true;
  1200. _emulationContext.System.TogglePauseEmulation(true);
  1201. Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version, "Paused");
  1202. Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
  1203. }
  1204. private void ResumeEmulation_Pressed(object sender, EventArgs args)
  1205. {
  1206. _pauseEmulation.Sensitive = true;
  1207. _resumeEmulation.Sensitive = false;
  1208. _emulationContext.System.TogglePauseEmulation(false);
  1209. Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version);
  1210. Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
  1211. }
  1212. public void ActivatePauseMenu()
  1213. {
  1214. _pauseEmulation.Sensitive = true;
  1215. _resumeEmulation.Sensitive = false;
  1216. }
  1217. public void TogglePause()
  1218. {
  1219. _pauseEmulation.Sensitive ^= true;
  1220. _resumeEmulation.Sensitive ^= true;
  1221. _emulationContext.System.TogglePauseEmulation(_resumeEmulation.Sensitive);
  1222. }
  1223. private void Installer_File_Pressed(object o, EventArgs args)
  1224. {
  1225. FileChooserNative fileChooser = new("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel");
  1226. FileFilter filter = new()
  1227. {
  1228. Name = "Switch Firmware Files",
  1229. };
  1230. filter.AddPattern("*.zip");
  1231. filter.AddPattern("*.xci");
  1232. fileChooser.AddFilter(filter);
  1233. HandleInstallerDialog(fileChooser);
  1234. }
  1235. private void Installer_Directory_Pressed(object o, EventArgs args)
  1236. {
  1237. FileChooserNative directoryChooser = new("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel");
  1238. HandleInstallerDialog(directoryChooser);
  1239. }
  1240. private void HandleInstallerDialog(FileChooserNative fileChooser)
  1241. {
  1242. if (fileChooser.Run() == (int)ResponseType.Accept)
  1243. {
  1244. try
  1245. {
  1246. string filename = fileChooser.Filename;
  1247. fileChooser.Dispose();
  1248. SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename);
  1249. if (firmwareVersion is null)
  1250. {
  1251. GtkDialog.CreateErrorDialog($"A valid system firmware was not found in {filename}.");
  1252. return;
  1253. }
  1254. string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}";
  1255. SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion();
  1256. string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed.";
  1257. if (currentVersion != null)
  1258. {
  1259. dialogMessage += $"\n\nThis will replace the current system version {currentVersion.VersionString}. ";
  1260. }
  1261. dialogMessage += "\n\nDo you want to continue?";
  1262. ResponseType responseInstallDialog = (ResponseType)GtkDialog.CreateConfirmationDialog(dialogTitle, dialogMessage).Run();
  1263. MessageDialog waitingDialog = GtkDialog.CreateWaitingDialog(dialogTitle, "Installing firmware...");
  1264. if (responseInstallDialog == ResponseType.Yes)
  1265. {
  1266. Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}");
  1267. Thread thread = new(() =>
  1268. {
  1269. Application.Invoke(delegate
  1270. {
  1271. waitingDialog.Run();
  1272. });
  1273. try
  1274. {
  1275. _contentManager.InstallFirmware(filename);
  1276. Application.Invoke(delegate
  1277. {
  1278. waitingDialog.Dispose();
  1279. string message = $"System version {firmwareVersion.VersionString} successfully installed.";
  1280. GtkDialog.CreateInfoDialog(dialogTitle, message);
  1281. Logger.Info?.Print(LogClass.Application, message);
  1282. // Purge Applet Cache.
  1283. DirectoryInfo miiEditorCacheFolder = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
  1284. if (miiEditorCacheFolder.Exists)
  1285. {
  1286. miiEditorCacheFolder.Delete(true);
  1287. }
  1288. });
  1289. }
  1290. catch (Exception ex)
  1291. {
  1292. Application.Invoke(delegate
  1293. {
  1294. waitingDialog.Dispose();
  1295. GtkDialog.CreateErrorDialog(ex.Message);
  1296. });
  1297. }
  1298. finally
  1299. {
  1300. RefreshFirmwareLabel();
  1301. }
  1302. })
  1303. {
  1304. Name = "GUI.FirmwareInstallerThread",
  1305. };
  1306. thread.Start();
  1307. }
  1308. }
  1309. catch (MissingKeyException ex)
  1310. {
  1311. Logger.Error?.Print(LogClass.Application, ex.ToString());
  1312. UserErrorDialog.CreateUserErrorDialog(UserError.FirmwareParsingFailed);
  1313. }
  1314. catch (Exception ex)
  1315. {
  1316. GtkDialog.CreateErrorDialog(ex.Message);
  1317. }
  1318. }
  1319. else
  1320. {
  1321. fileChooser.Dispose();
  1322. }
  1323. }
  1324. private void RefreshFirmwareLabel()
  1325. {
  1326. SystemVersion currentFirmware = _contentManager.GetCurrentFirmwareVersion();
  1327. Application.Invoke(delegate
  1328. {
  1329. _firmwareVersionLabel.Text = currentFirmware != null ? currentFirmware.VersionString : "0.0.0";
  1330. });
  1331. }
  1332. private void InstallFileTypes_Pressed(object sender, EventArgs e)
  1333. {
  1334. if (FileAssociationHelper.Install())
  1335. {
  1336. GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!");
  1337. }
  1338. else
  1339. {
  1340. GtkDialog.CreateErrorDialog("Failed to install file types.");
  1341. }
  1342. }
  1343. private void UninstallFileTypes_Pressed(object sender, EventArgs e)
  1344. {
  1345. if (FileAssociationHelper.Uninstall())
  1346. {
  1347. GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!");
  1348. }
  1349. else
  1350. {
  1351. GtkDialog.CreateErrorDialog("Failed to uninstall file types.");
  1352. }
  1353. }
  1354. private void HandleRelaunch()
  1355. {
  1356. if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)
  1357. {
  1358. _userChannelPersistence.ShouldRestart = false;
  1359. RunApplication(_currentEmulatedGamePath);
  1360. }
  1361. else
  1362. {
  1363. // otherwise, clear state.
  1364. _userChannelPersistence = new UserChannelPersistence();
  1365. _currentEmulatedGamePath = null;
  1366. _actionMenu.Sensitive = false;
  1367. _firmwareInstallFile.Sensitive = true;
  1368. _firmwareInstallDirectory.Sensitive = true;
  1369. }
  1370. }
  1371. private void FullScreen_Toggled(object sender, EventArgs args)
  1372. {
  1373. if (!Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  1374. {
  1375. Fullscreen();
  1376. ToggleExtraWidgets(false);
  1377. }
  1378. else
  1379. {
  1380. Unfullscreen();
  1381. ToggleExtraWidgets(true);
  1382. }
  1383. }
  1384. private void StartFullScreen_Toggled(object sender, EventArgs args)
  1385. {
  1386. ConfigurationState.Instance.UI.StartFullscreen.Value = _startFullScreen.Active;
  1387. SaveConfig();
  1388. }
  1389. private void ShowConsole_Toggled(object sender, EventArgs args)
  1390. {
  1391. ConfigurationState.Instance.UI.ShowConsole.Value = _showConsole.Active;
  1392. SaveConfig();
  1393. }
  1394. private void OptionMenu_StateChanged(object o, StateChangedArgs args)
  1395. {
  1396. _manageUserProfiles.Sensitive = _emulationContext == null;
  1397. }
  1398. private void Settings_Pressed(object sender, EventArgs args)
  1399. {
  1400. SettingsWindow settingsWindow = new(this, _virtualFileSystem, _contentManager);
  1401. settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor));
  1402. settingsWindow.Show();
  1403. }
  1404. private void HideUI_Pressed(object sender, EventArgs args)
  1405. {
  1406. ToggleExtraWidgets(false);
  1407. }
  1408. private void ManageCheats_Pressed(object sender, EventArgs args)
  1409. {
  1410. var window = new CheatWindow(
  1411. _virtualFileSystem,
  1412. _emulationContext.Processes.ActiveApplication.ProgramId,
  1413. _emulationContext.Processes.ActiveApplication.ApplicationControlProperties
  1414. .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
  1415. _currentEmulatedGamePath);
  1416. window.Destroyed += CheatWindow_Destroyed;
  1417. window.Show();
  1418. }
  1419. private void CheatWindow_Destroyed(object sender, EventArgs e)
  1420. {
  1421. _emulationContext.EnableCheats();
  1422. (sender as CheatWindow).Destroyed -= CheatWindow_Destroyed;
  1423. }
  1424. private void ManageUserProfiles_Pressed(object sender, EventArgs args)
  1425. {
  1426. UserProfilesManagerWindow userProfilesManagerWindow = new(_accountManager, _contentManager, _virtualFileSystem);
  1427. userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor));
  1428. userProfilesManagerWindow.Show();
  1429. }
  1430. private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)
  1431. {
  1432. _emulationContext?.System.SimulateWakeUpMessage();
  1433. }
  1434. private void ActionMenu_StateChanged(object o, StateChangedArgs args)
  1435. {
  1436. _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _);
  1437. _takeScreenshot.Sensitive = _emulationContext != null;
  1438. }
  1439. private void Scan_Amiibo(object sender, EventArgs args)
  1440. {
  1441. if (_emulationContext.System.SearchingForAmiibo(out int deviceId))
  1442. {
  1443. AmiiboWindow amiiboWindow = new()
  1444. {
  1445. LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll,
  1446. LastScannedAmiiboId = _lastScannedAmiiboId,
  1447. DeviceId = deviceId,
  1448. TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper(),
  1449. };
  1450. amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent;
  1451. amiiboWindow.Show();
  1452. }
  1453. else
  1454. {
  1455. GtkDialog.CreateInfoDialog($"Amiibo", "The game is currently not ready to receive Amiibo scan data. Ensure that you have an Amiibo-compatible game open and ready to receive Amiibo scan data.");
  1456. }
  1457. }
  1458. private void Take_Screenshot(object sender, EventArgs args)
  1459. {
  1460. if (_emulationContext != null && RendererWidget != null)
  1461. {
  1462. RendererWidget.ScreenshotRequested = true;
  1463. }
  1464. }
  1465. private void AmiiboWindow_DeleteEvent(object sender, DeleteEventArgs args)
  1466. {
  1467. if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok)
  1468. {
  1469. _lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId;
  1470. _lastScannedAmiiboShowAll = ((AmiiboWindow)sender).LastScannedAmiiboShowAll;
  1471. _emulationContext.System.ScanAmiibo(((AmiiboWindow)sender).DeviceId, ((AmiiboWindow)sender).AmiiboId, ((AmiiboWindow)sender).UseRandomUuid);
  1472. }
  1473. }
  1474. private void Update_Pressed(object sender, EventArgs args)
  1475. {
  1476. if (Updater.CanUpdate(true))
  1477. {
  1478. Updater.BeginParse(this, true).ContinueWith(task =>
  1479. {
  1480. Logger.Error?.Print(LogClass.Application, $"Updater error: {task.Exception}");
  1481. }, TaskContinuationOptions.OnlyOnFaulted);
  1482. }
  1483. }
  1484. private void About_Pressed(object sender, EventArgs args)
  1485. {
  1486. AboutWindow aboutWindow = new();
  1487. aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor));
  1488. aboutWindow.Show();
  1489. }
  1490. private void Fav_Toggled(object sender, EventArgs args)
  1491. {
  1492. ConfigurationState.Instance.UI.GuiColumns.FavColumn.Value = _favToggle.Active;
  1493. SaveConfig();
  1494. UpdateColumns();
  1495. }
  1496. private void Icon_Toggled(object sender, EventArgs args)
  1497. {
  1498. ConfigurationState.Instance.UI.GuiColumns.IconColumn.Value = _iconToggle.Active;
  1499. SaveConfig();
  1500. UpdateColumns();
  1501. }
  1502. private void App_Toggled(object sender, EventArgs args)
  1503. {
  1504. ConfigurationState.Instance.UI.GuiColumns.AppColumn.Value = _appToggle.Active;
  1505. SaveConfig();
  1506. UpdateColumns();
  1507. }
  1508. private void Developer_Toggled(object sender, EventArgs args)
  1509. {
  1510. ConfigurationState.Instance.UI.GuiColumns.DevColumn.Value = _developerToggle.Active;
  1511. SaveConfig();
  1512. UpdateColumns();
  1513. }
  1514. private void Version_Toggled(object sender, EventArgs args)
  1515. {
  1516. ConfigurationState.Instance.UI.GuiColumns.VersionColumn.Value = _versionToggle.Active;
  1517. SaveConfig();
  1518. UpdateColumns();
  1519. }
  1520. private void TimePlayed_Toggled(object sender, EventArgs args)
  1521. {
  1522. ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
  1523. SaveConfig();
  1524. UpdateColumns();
  1525. }
  1526. private void LastPlayed_Toggled(object sender, EventArgs args)
  1527. {
  1528. ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
  1529. SaveConfig();
  1530. UpdateColumns();
  1531. }
  1532. private void FileExt_Toggled(object sender, EventArgs args)
  1533. {
  1534. ConfigurationState.Instance.UI.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
  1535. SaveConfig();
  1536. UpdateColumns();
  1537. }
  1538. private void FileSize_Toggled(object sender, EventArgs args)
  1539. {
  1540. ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
  1541. SaveConfig();
  1542. UpdateColumns();
  1543. }
  1544. private void Path_Toggled(object sender, EventArgs args)
  1545. {
  1546. ConfigurationState.Instance.UI.GuiColumns.PathColumn.Value = _pathToggle.Active;
  1547. SaveConfig();
  1548. UpdateColumns();
  1549. }
  1550. private void NSP_Shown_Toggled(object sender, EventArgs args)
  1551. {
  1552. ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = _nspShown.Active;
  1553. SaveConfig();
  1554. UpdateGameTable();
  1555. }
  1556. private void PFS0_Shown_Toggled(object sender, EventArgs args)
  1557. {
  1558. ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = _pfs0Shown.Active;
  1559. SaveConfig();
  1560. UpdateGameTable();
  1561. }
  1562. private void XCI_Shown_Toggled(object sender, EventArgs args)
  1563. {
  1564. ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = _xciShown.Active;
  1565. SaveConfig();
  1566. UpdateGameTable();
  1567. }
  1568. private void NCA_Shown_Toggled(object sender, EventArgs args)
  1569. {
  1570. ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = _ncaShown.Active;
  1571. SaveConfig();
  1572. UpdateGameTable();
  1573. }
  1574. private void NRO_Shown_Toggled(object sender, EventArgs args)
  1575. {
  1576. ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = _nroShown.Active;
  1577. SaveConfig();
  1578. UpdateGameTable();
  1579. }
  1580. private void NSO_Shown_Toggled(object sender, EventArgs args)
  1581. {
  1582. ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = _nsoShown.Active;
  1583. SaveConfig();
  1584. UpdateGameTable();
  1585. }
  1586. private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args)
  1587. {
  1588. UpdateGameTable();
  1589. }
  1590. }
  1591. }