SettingsViewModel.cs 29 KB

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