SettingsViewModel.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. using Avalonia.Collections;
  2. using Avalonia.Controls;
  3. using Avalonia.Threading;
  4. using LibHac.Tools.FsSystem;
  5. using Ryujinx.Audio.Backends.OpenAL;
  6. using Ryujinx.Audio.Backends.SDL2;
  7. using Ryujinx.Audio.Backends.SoundIo;
  8. using Ryujinx.Ava.Common.Locale;
  9. using Ryujinx.Ava.UI.Helpers;
  10. using Ryujinx.Ava.UI.Models.Input;
  11. using Ryujinx.Ava.UI.Windows;
  12. using Ryujinx.Common.Configuration;
  13. using Ryujinx.Common.Configuration.Multiplayer;
  14. using Ryujinx.Common.GraphicsDriver;
  15. using Ryujinx.Common.Logging;
  16. using Ryujinx.Graphics.Vulkan;
  17. using Ryujinx.HLE;
  18. using Ryujinx.HLE.FileSystem;
  19. using Ryujinx.HLE.HOS.Services.Time.TimeZone;
  20. using Ryujinx.UI.Common.Configuration;
  21. using Ryujinx.UI.Common.Configuration.System;
  22. using System;
  23. using System.Collections.Generic;
  24. using System.Collections.ObjectModel;
  25. using System.Linq;
  26. using System.Net.NetworkInformation;
  27. using System.Runtime.InteropServices;
  28. using System.Text.RegularExpressions;
  29. using System.Threading.Tasks;
  30. using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
  31. namespace Ryujinx.Ava.UI.ViewModels
  32. {
  33. public partial class SettingsViewModel : BaseModel
  34. {
  35. private readonly VirtualFileSystem _virtualFileSystem;
  36. private readonly ContentManager _contentManager;
  37. private TimeZoneContentManager _timeZoneContentManager;
  38. private readonly List<string> _validTzRegions;
  39. private readonly Dictionary<string, string> _networkInterfaces;
  40. private float _customResolutionScale;
  41. private int _resolutionScale;
  42. private int _graphicsBackendMultithreadingIndex;
  43. private float _volume;
  44. private bool _isVulkanAvailable = true;
  45. private bool _gameDirectoryChanged;
  46. private bool _autoloadDirectoryChanged;
  47. private readonly List<string> _gpuIds = new();
  48. private int _graphicsBackendIndex;
  49. private int _scalingFilter;
  50. private int _scalingFilterLevel;
  51. private int _customVSyncInterval;
  52. private bool _enableCustomVSyncInterval;
  53. private int _customVSyncIntervalPercentageProxy;
  54. private VSyncMode _vSyncMode;
  55. public event Action CloseWindow;
  56. public event Action SaveSettingsEvent;
  57. private int _networkInterfaceIndex;
  58. private int _multiplayerModeIndex;
  59. private string _ldnPassphrase;
  60. private string _LdnServer;
  61. public int ResolutionScale
  62. {
  63. get => _resolutionScale;
  64. set
  65. {
  66. _resolutionScale = value;
  67. OnPropertyChanged(nameof(CustomResolutionScale));
  68. OnPropertyChanged(nameof(IsCustomResolutionScaleActive));
  69. }
  70. }
  71. public int GraphicsBackendMultithreadingIndex
  72. {
  73. get => _graphicsBackendMultithreadingIndex;
  74. set
  75. {
  76. _graphicsBackendMultithreadingIndex = value;
  77. if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
  78. {
  79. Dispatcher.UIThread.InvokeAsync(() =>
  80. ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
  81. string.Empty,
  82. string.Empty,
  83. LocaleManager.Instance[LocaleKeys.InputDialogOk],
  84. LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle])
  85. );
  86. }
  87. OnPropertyChanged();
  88. }
  89. }
  90. public float CustomResolutionScale
  91. {
  92. get => _customResolutionScale;
  93. set
  94. {
  95. _customResolutionScale = MathF.Round(value, 1);
  96. OnPropertyChanged();
  97. }
  98. }
  99. public bool IsVulkanAvailable
  100. {
  101. get => _isVulkanAvailable;
  102. set
  103. {
  104. _isVulkanAvailable = value;
  105. OnPropertyChanged();
  106. }
  107. }
  108. public bool IsOpenGLAvailable => !OperatingSystem.IsMacOS();
  109. public bool IsHypervisorAvailable => OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
  110. public bool GameDirectoryChanged
  111. {
  112. get => _gameDirectoryChanged;
  113. set
  114. {
  115. _gameDirectoryChanged = value;
  116. OnPropertyChanged();
  117. }
  118. }
  119. public bool AutoloadDirectoryChanged
  120. {
  121. get => _autoloadDirectoryChanged;
  122. set
  123. {
  124. _autoloadDirectoryChanged = value;
  125. OnPropertyChanged();
  126. }
  127. }
  128. public bool IsMacOS => OperatingSystem.IsMacOS();
  129. public bool EnableDiscordIntegration { get; set; }
  130. public bool CheckUpdatesOnStart { get; set; }
  131. public bool ShowConfirmExit { get; set; }
  132. public bool IgnoreApplet { get; set; }
  133. public bool RememberWindowState { get; set; }
  134. public bool ShowTitleBar { get; set; }
  135. public int HideCursor { get; set; }
  136. public bool EnableDockedMode { get; set; }
  137. public bool EnableKeyboard { get; set; }
  138. public bool EnableMouse { get; set; }
  139. public VSyncMode VSyncMode
  140. {
  141. get => _vSyncMode;
  142. set
  143. {
  144. if (value == VSyncMode.Custom ||
  145. value == VSyncMode.Switch ||
  146. value == VSyncMode.Unbounded)
  147. {
  148. _vSyncMode = value;
  149. OnPropertyChanged();
  150. }
  151. }
  152. }
  153. public int CustomVSyncIntervalPercentageProxy
  154. {
  155. get => _customVSyncIntervalPercentageProxy;
  156. set
  157. {
  158. int newInterval = (int)((value / 100f) * 60);
  159. _customVSyncInterval = newInterval;
  160. _customVSyncIntervalPercentageProxy = value;
  161. OnPropertyChanged((nameof(CustomVSyncInterval)));
  162. OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText)));
  163. }
  164. }
  165. public string CustomVSyncIntervalPercentageText
  166. {
  167. get
  168. {
  169. string text = CustomVSyncIntervalPercentageProxy.ToString() + "%";
  170. return text;
  171. }
  172. }
  173. public bool EnableCustomVSyncInterval
  174. {
  175. get => _enableCustomVSyncInterval;
  176. set
  177. {
  178. _enableCustomVSyncInterval = value;
  179. if (_vSyncMode == VSyncMode.Custom && !value)
  180. {
  181. VSyncMode = VSyncMode.Switch;
  182. }
  183. else if (value)
  184. {
  185. VSyncMode = VSyncMode.Custom;
  186. }
  187. OnPropertyChanged();
  188. }
  189. }
  190. public int CustomVSyncInterval
  191. {
  192. get => _customVSyncInterval;
  193. set
  194. {
  195. _customVSyncInterval = value;
  196. int newPercent = (int)((value / 60f) * 100);
  197. _customVSyncIntervalPercentageProxy = newPercent;
  198. OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy));
  199. OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText));
  200. OnPropertyChanged();
  201. }
  202. }
  203. public bool EnablePptc { get; set; }
  204. public bool EnableLowPowerPptc { get; set; }
  205. public bool EnableInternetAccess { get; set; }
  206. public bool EnableFsIntegrityChecks { get; set; }
  207. public bool IgnoreMissingServices { get; set; }
  208. public MemoryConfiguration DramSize { get; set; }
  209. public bool EnableShaderCache { get; set; }
  210. public bool EnableTextureRecompression { get; set; }
  211. public bool EnableMacroHLE { get; set; }
  212. public bool EnableColorSpacePassthrough { get; set; }
  213. public bool ColorSpacePassthroughAvailable => IsMacOS;
  214. public bool EnableFileLog { get; set; }
  215. public bool EnableStub { get; set; }
  216. public bool EnableInfo { get; set; }
  217. public bool EnableWarn { get; set; }
  218. public bool EnableError { get; set; }
  219. public bool EnableTrace { get; set; }
  220. public bool EnableGuest { get; set; }
  221. public bool EnableFsAccessLog { get; set; }
  222. public bool EnableDebug { get; set; }
  223. public bool IsOpenAlEnabled { get; set; }
  224. public bool IsSoundIoEnabled { get; set; }
  225. public bool IsSDL2Enabled { get; set; }
  226. public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
  227. public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
  228. public bool IsVulkanSelected => GraphicsBackendIndex == 0;
  229. public bool UseHypervisor { get; set; }
  230. public bool DisableP2P { get; set; }
  231. public string TimeZone { get; set; }
  232. public string ShaderDumpPath { get; set; }
  233. public string LdnPassphrase
  234. {
  235. get => _ldnPassphrase;
  236. set
  237. {
  238. _ldnPassphrase = value;
  239. IsInvalidLdnPassphraseVisible = !ValidateLdnPassphrase(value);
  240. OnPropertyChanged();
  241. OnPropertyChanged(nameof(IsInvalidLdnPassphraseVisible));
  242. }
  243. }
  244. public int Language { get; set; }
  245. public int Region { get; set; }
  246. public int FsGlobalAccessLogMode { get; set; }
  247. public int AudioBackend { get; set; }
  248. public int MaxAnisotropy { get; set; }
  249. public int AspectRatio { get; set; }
  250. public int AntiAliasingEffect { get; set; }
  251. public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0");
  252. public int ScalingFilterLevel
  253. {
  254. get => _scalingFilterLevel;
  255. set
  256. {
  257. _scalingFilterLevel = value;
  258. OnPropertyChanged();
  259. OnPropertyChanged(nameof(ScalingFilterLevelText));
  260. }
  261. }
  262. public int OpenglDebugLevel { get; set; }
  263. public int MemoryMode { get; set; }
  264. public int BaseStyleIndex { get; set; }
  265. public int GraphicsBackendIndex
  266. {
  267. get => _graphicsBackendIndex;
  268. set
  269. {
  270. _graphicsBackendIndex = value;
  271. OnPropertyChanged();
  272. OnPropertyChanged(nameof(IsVulkanSelected));
  273. }
  274. }
  275. public int ScalingFilter
  276. {
  277. get => _scalingFilter;
  278. set
  279. {
  280. _scalingFilter = value;
  281. OnPropertyChanged();
  282. OnPropertyChanged(nameof(IsScalingFilterActive));
  283. }
  284. }
  285. public int PreferredGpuIndex { get; set; }
  286. public float Volume
  287. {
  288. get => _volume;
  289. set
  290. {
  291. _volume = value;
  292. ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100;
  293. OnPropertyChanged();
  294. }
  295. }
  296. public DateTimeOffset CurrentDate { get; set; }
  297. public TimeSpan CurrentTime { get; set; }
  298. internal AvaloniaList<TimeZone> TimeZones { get; set; }
  299. public AvaloniaList<string> GameDirectories { get; set; }
  300. public AvaloniaList<string> AutoloadDirectories { get; set; }
  301. public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
  302. public AvaloniaList<string> NetworkInterfaceList
  303. {
  304. get => new(_networkInterfaces.Keys);
  305. }
  306. public HotkeyConfig KeyboardHotkey { get; set; }
  307. public int NetworkInterfaceIndex
  308. {
  309. get => _networkInterfaceIndex;
  310. set
  311. {
  312. _networkInterfaceIndex = value != -1 ? value : 0;
  313. ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[_networkInterfaceIndex]];
  314. }
  315. }
  316. public int MultiplayerModeIndex
  317. {
  318. get => _multiplayerModeIndex;
  319. set
  320. {
  321. _multiplayerModeIndex = value;
  322. ConfigurationState.Instance.Multiplayer.Mode.Value = (MultiplayerMode)_multiplayerModeIndex;
  323. }
  324. }
  325. [GeneratedRegex("Ryujinx-[0-9a-f]{8}")]
  326. private static partial Regex LdnPassphraseRegex();
  327. public bool IsInvalidLdnPassphraseVisible { get; set; }
  328. public string LdnServer
  329. {
  330. get => _LdnServer;
  331. set
  332. {
  333. _LdnServer = value;
  334. OnPropertyChanged();
  335. }
  336. }
  337. public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
  338. {
  339. _virtualFileSystem = virtualFileSystem;
  340. _contentManager = contentManager;
  341. if (Program.PreviewerDetached)
  342. {
  343. Task.Run(LoadTimeZones);
  344. }
  345. }
  346. public SettingsViewModel()
  347. {
  348. GameDirectories = [];
  349. AutoloadDirectories = [];
  350. TimeZones = [];
  351. AvailableGpus = [];
  352. _validTzRegions = [];
  353. _networkInterfaces = new Dictionary<string, string>();
  354. Task.Run(CheckSoundBackends);
  355. Task.Run(PopulateNetworkInterfaces);
  356. if (Program.PreviewerDetached)
  357. {
  358. Task.Run(LoadAvailableGpus);
  359. LoadCurrentConfiguration();
  360. }
  361. }
  362. public async Task CheckSoundBackends()
  363. {
  364. IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
  365. IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
  366. IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
  367. await Dispatcher.UIThread.InvokeAsync(() =>
  368. {
  369. OnPropertyChanged(nameof(IsOpenAlEnabled));
  370. OnPropertyChanged(nameof(IsSoundIoEnabled));
  371. OnPropertyChanged(nameof(IsSDL2Enabled));
  372. });
  373. }
  374. private async Task LoadAvailableGpus()
  375. {
  376. AvailableGpus.Clear();
  377. var devices = VulkanRenderer.GetPhysicalDevices();
  378. if (devices.Length == 0)
  379. {
  380. IsVulkanAvailable = false;
  381. GraphicsBackendIndex = 1;
  382. }
  383. else
  384. {
  385. foreach (var device in devices)
  386. {
  387. await Dispatcher.UIThread.InvokeAsync(() =>
  388. {
  389. _gpuIds.Add(device.Id);
  390. AvailableGpus.Add(new ComboBoxItem { Content = $"{device.Name} {(device.IsDiscrete ? "(dGPU)" : string.Empty)}" });
  391. });
  392. }
  393. }
  394. // GPU configuration needs to be loaded during the async method or it will always return 0.
  395. PreferredGpuIndex = _gpuIds.Contains(ConfigurationState.Instance.Graphics.PreferredGpu) ?
  396. _gpuIds.IndexOf(ConfigurationState.Instance.Graphics.PreferredGpu) : 0;
  397. Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(PreferredGpuIndex)));
  398. }
  399. public void MatchSystemTime()
  400. {
  401. var dto = DateTimeOffset.Now;
  402. CurrentDate = new DateTimeOffset(dto.Year, dto.Month, dto.Day, 0, 0, 0, dto.Offset);
  403. CurrentTime = dto.TimeOfDay;
  404. OnPropertyChanged(nameof(CurrentDate));
  405. OnPropertyChanged(nameof(CurrentTime));
  406. }
  407. public async Task LoadTimeZones()
  408. {
  409. _timeZoneContentManager = new TimeZoneContentManager();
  410. _timeZoneContentManager.InitializeInstance(_virtualFileSystem, _contentManager, IntegrityCheckLevel.None);
  411. foreach ((int offset, string location, string abbr) in _timeZoneContentManager.ParseTzOffsets())
  412. {
  413. int hours = Math.DivRem(offset, 3600, out int seconds);
  414. int minutes = Math.Abs(seconds) / 60;
  415. string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr;
  416. await Dispatcher.UIThread.InvokeAsync(() =>
  417. {
  418. TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2));
  419. _validTzRegions.Add(location);
  420. });
  421. }
  422. Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(TimeZone)));
  423. }
  424. private async Task PopulateNetworkInterfaces()
  425. {
  426. _networkInterfaces.Clear();
  427. _networkInterfaces.Add(LocaleManager.Instance[LocaleKeys.NetworkInterfaceDefault], "0");
  428. foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
  429. {
  430. await Dispatcher.UIThread.InvokeAsync(() =>
  431. {
  432. _networkInterfaces.Add(networkInterface.Name, networkInterface.Id);
  433. });
  434. }
  435. // Network interface index needs to be loaded during the async method or it will always return 0.
  436. NetworkInterfaceIndex = _networkInterfaces.Values.ToList().IndexOf(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value);
  437. Dispatcher.UIThread.Post(() => OnPropertyChanged(nameof(NetworkInterfaceIndex)));
  438. }
  439. private bool ValidateLdnPassphrase(string passphrase)
  440. {
  441. return string.IsNullOrEmpty(passphrase) || (passphrase.Length == 16 && LdnPassphraseRegex().IsMatch(passphrase));
  442. }
  443. public void ValidateAndSetTimeZone(string location)
  444. {
  445. if (_validTzRegions.Contains(location))
  446. {
  447. TimeZone = location;
  448. }
  449. }
  450. public void LoadCurrentConfiguration()
  451. {
  452. ConfigurationState config = ConfigurationState.Instance;
  453. // User Interface
  454. EnableDiscordIntegration = config.EnableDiscordIntegration;
  455. CheckUpdatesOnStart = config.CheckUpdatesOnStart;
  456. ShowConfirmExit = config.ShowConfirmExit;
  457. IgnoreApplet = config.IgnoreApplet;
  458. RememberWindowState = config.RememberWindowState;
  459. ShowTitleBar = config.ShowTitleBar;
  460. HideCursor = (int)config.HideCursor.Value;
  461. GameDirectories.Clear();
  462. GameDirectories.AddRange(config.UI.GameDirs.Value);
  463. AutoloadDirectories.Clear();
  464. AutoloadDirectories.AddRange(config.UI.AutoloadDirs.Value);
  465. BaseStyleIndex = config.UI.BaseStyle.Value switch
  466. {
  467. "Auto" => 0,
  468. "Light" => 1,
  469. "Dark" => 2,
  470. _ => 0
  471. };
  472. // Input
  473. EnableDockedMode = config.System.EnableDockedMode;
  474. EnableKeyboard = config.Hid.EnableKeyboard;
  475. EnableMouse = config.Hid.EnableMouse;
  476. // Keyboard Hotkeys
  477. KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value);
  478. // System
  479. Region = (int)config.System.Region.Value;
  480. Language = (int)config.System.Language.Value;
  481. TimeZone = config.System.TimeZone;
  482. DateTime currentHostDateTime = DateTime.Now;
  483. TimeSpan systemDateTimeOffset = TimeSpan.FromSeconds(config.System.SystemTimeOffset);
  484. DateTime currentDateTime = currentHostDateTime.Add(systemDateTimeOffset);
  485. CurrentDate = currentDateTime.Date;
  486. CurrentTime = currentDateTime.TimeOfDay;
  487. EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval.Value;
  488. CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
  489. VSyncMode = config.Graphics.VSyncMode;
  490. EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
  491. DramSize = config.System.DramSize;
  492. IgnoreMissingServices = config.System.IgnoreMissingServices;
  493. // CPU
  494. EnablePptc = config.System.EnablePtc;
  495. EnableLowPowerPptc = config.System.EnableLowPowerPtc;
  496. MemoryMode = (int)config.System.MemoryManagerMode.Value;
  497. UseHypervisor = config.System.UseHypervisor;
  498. // Graphics
  499. GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
  500. // Physical devices are queried asynchronously hence the preferred index config value is loaded in LoadAvailableGpus().
  501. EnableShaderCache = config.Graphics.EnableShaderCache;
  502. EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
  503. EnableMacroHLE = config.Graphics.EnableMacroHLE;
  504. EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough;
  505. ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
  506. CustomResolutionScale = config.Graphics.ResScaleCustom;
  507. MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)MathF.Log2(config.Graphics.MaxAnisotropy);
  508. AspectRatio = (int)config.Graphics.AspectRatio.Value;
  509. GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
  510. ShaderDumpPath = config.Graphics.ShadersDumpPath;
  511. AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value;
  512. ScalingFilter = (int)config.Graphics.ScalingFilter.Value;
  513. ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value;
  514. // Audio
  515. AudioBackend = (int)config.System.AudioBackend.Value;
  516. Volume = config.System.AudioVolume * 100;
  517. // Network
  518. EnableInternetAccess = config.System.EnableInternetAccess;
  519. // LAN interface index is loaded asynchronously in PopulateNetworkInterfaces()
  520. // Logging
  521. EnableFileLog = config.Logger.EnableFileLog;
  522. EnableStub = config.Logger.EnableStub;
  523. EnableInfo = config.Logger.EnableInfo;
  524. EnableWarn = config.Logger.EnableWarn;
  525. EnableError = config.Logger.EnableError;
  526. EnableTrace = config.Logger.EnableTrace;
  527. EnableGuest = config.Logger.EnableGuest;
  528. EnableDebug = config.Logger.EnableDebug;
  529. EnableFsAccessLog = config.Logger.EnableFsAccessLog;
  530. FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
  531. OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
  532. MultiplayerModeIndex = (int)config.Multiplayer.Mode.Value;
  533. DisableP2P = config.Multiplayer.DisableP2p.Value;
  534. LdnPassphrase = config.Multiplayer.LdnPassphrase.Value;
  535. LdnServer = config.Multiplayer.LdnServer.Value;
  536. }
  537. public void SaveSettings()
  538. {
  539. ConfigurationState config = ConfigurationState.Instance;
  540. // User Interface
  541. config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
  542. config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
  543. config.ShowConfirmExit.Value = ShowConfirmExit;
  544. config.IgnoreApplet.Value = IgnoreApplet;
  545. config.RememberWindowState.Value = RememberWindowState;
  546. config.ShowTitleBar.Value = ShowTitleBar;
  547. config.HideCursor.Value = (HideCursorMode)HideCursor;
  548. if (_gameDirectoryChanged)
  549. {
  550. List<string> gameDirs = new(GameDirectories);
  551. config.UI.GameDirs.Value = gameDirs;
  552. }
  553. if (_autoloadDirectoryChanged)
  554. {
  555. List<string> autoloadDirs = new(AutoloadDirectories);
  556. config.UI.AutoloadDirs.Value = autoloadDirs;
  557. }
  558. config.UI.BaseStyle.Value = BaseStyleIndex switch
  559. {
  560. 0 => "Auto",
  561. 1 => "Light",
  562. 2 => "Dark",
  563. _ => "Auto"
  564. };
  565. // Input
  566. config.System.EnableDockedMode.Value = EnableDockedMode;
  567. config.Hid.EnableKeyboard.Value = EnableKeyboard;
  568. config.Hid.EnableMouse.Value = EnableMouse;
  569. // Keyboard Hotkeys
  570. config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig();
  571. // System
  572. config.System.Region.Value = (Region)Region;
  573. config.System.Language.Value = (Language)Language;
  574. if (_validTzRegions.Contains(TimeZone))
  575. {
  576. config.System.TimeZone.Value = TimeZone;
  577. }
  578. config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
  579. config.Graphics.VSyncMode.Value = VSyncMode;
  580. config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval;
  581. config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval;
  582. config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
  583. config.System.DramSize.Value = DramSize;
  584. config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
  585. // CPU
  586. config.System.EnablePtc.Value = EnablePptc;
  587. config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc;
  588. config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
  589. config.System.UseHypervisor.Value = UseHypervisor;
  590. // Graphics
  591. config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
  592. config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
  593. config.Graphics.EnableShaderCache.Value = EnableShaderCache;
  594. config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
  595. config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
  596. config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough;
  597. config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
  598. config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
  599. config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
  600. config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
  601. config.Graphics.AntiAliasing.Value = (AntiAliasing)AntiAliasingEffect;
  602. config.Graphics.ScalingFilter.Value = (ScalingFilter)ScalingFilter;
  603. config.Graphics.ScalingFilterLevel.Value = ScalingFilterLevel;
  604. if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
  605. {
  606. DriverUtilities.ToggleOGLThreading(GraphicsBackendMultithreadingIndex == (int)BackendThreading.Off);
  607. }
  608. config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex;
  609. config.Graphics.ShadersDumpPath.Value = ShaderDumpPath;
  610. // Audio
  611. AudioBackend audioBackend = (AudioBackend)AudioBackend;
  612. if (audioBackend != config.System.AudioBackend.Value)
  613. {
  614. config.System.AudioBackend.Value = audioBackend;
  615. Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
  616. }
  617. config.System.AudioVolume.Value = Volume / 100;
  618. // Network
  619. config.System.EnableInternetAccess.Value = EnableInternetAccess;
  620. // Logging
  621. config.Logger.EnableFileLog.Value = EnableFileLog;
  622. config.Logger.EnableStub.Value = EnableStub;
  623. config.Logger.EnableInfo.Value = EnableInfo;
  624. config.Logger.EnableWarn.Value = EnableWarn;
  625. config.Logger.EnableError.Value = EnableError;
  626. config.Logger.EnableTrace.Value = EnableTrace;
  627. config.Logger.EnableGuest.Value = EnableGuest;
  628. config.Logger.EnableDebug.Value = EnableDebug;
  629. config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
  630. config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
  631. config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
  632. config.Multiplayer.LanInterfaceId.Value = _networkInterfaces[NetworkInterfaceList[NetworkInterfaceIndex]];
  633. config.Multiplayer.Mode.Value = (MultiplayerMode)MultiplayerModeIndex;
  634. config.Multiplayer.DisableP2p.Value = DisableP2P;
  635. config.Multiplayer.LdnPassphrase.Value = LdnPassphrase;
  636. config.Multiplayer.LdnServer.Value = LdnServer;
  637. config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
  638. MainWindow.UpdateGraphicsConfig();
  639. MainWindow.MainWindowViewModel.VSyncModeSettingChanged();
  640. SaveSettingsEvent?.Invoke();
  641. _gameDirectoryChanged = false;
  642. _autoloadDirectoryChanged = false;
  643. }
  644. private static void RevertIfNotSaved()
  645. {
  646. Program.ReloadConfig();
  647. }
  648. public void ApplyButton()
  649. {
  650. SaveSettings();
  651. }
  652. public void OkButton()
  653. {
  654. SaveSettings();
  655. CloseWindow?.Invoke();
  656. }
  657. public void CancelButton()
  658. {
  659. RevertIfNotSaved();
  660. CloseWindow?.Invoke();
  661. }
  662. }
  663. }